每日總結-05-19(AC自己主動機結束)
今天下午討論了一下校賽的題,最終最終拍板,把校賽的題目定下來了。
然後今天A掉了4個AC自己主動機的題目。最終完畢了AC自己主動機專輯裏面的15個題。至此AC自己主動機全然結束。
明天開啟線段樹專題。
。。。。
------------------------------------------------------------------------------------------------------------------------------------
1,,hdu-2457-DNA repair
題目:給出一些不合法的模式DNA串,給出一個原串,問最少須要改動多少個字符,使得原串中不包括非法串
做法:把病毒串做成一個trie樹。
DP[i][j]:長度為i,在trie樹上的狀態為j。須要改變的最少的字符的個數。
dp[i][j]=dp[i-1][k]+轉移費用。
#include<stdio.h> #include<algorithm> #include<iostream> #include<stdlib.h> #include<vector> #include<queue> #include<string.h> #include<math.h> using namespace std; #define LL __int64 #define maxn 55 const int maxnode=55*21; const int childnum=5; const int mod=1000000007; const int INF=99999999; struct ac_tree { int chd[maxnode][childnum]; int val[maxnode]; int fail[maxnode]; int key[1<<11]; int Q[maxnode]; int ID[128]; int sz; int dp[1100][maxnode]; void init() { fail[0]=0; for(int i=0;i<childnum;i++) { ID[i]=i; } ID[‘A‘]=1;ID[‘G‘]=2; ID[‘C‘]=3;ID[‘T‘]=4; } void reset() { memset(chd,0,sizeof(chd)); sz=1; } void insert(char str[],int k) { int p=0; int len=strlen(str); for(int i=0;i<len;i++) { int c=ID[str[i]]; if(!chd[p][c]) { memset(chd[sz],0,sizeof(chd[sz])); val[sz]=0; chd[p][c]=sz++; } p=chd[p][c]; } val[p]=k; } void ac_build() { int *s=Q,*e=Q; for(int i=1;i<childnum;i++) { if(chd[0][i]) { fail[chd[0][i]]=0; *e++=chd[0][i]; } } while(s!=e) { int u=*s++; if(val[fail[u]])val[u]+=val[fail[u]]; for(int i=1;i<childnum;i++) { int &v=chd[u][i]; if(v) { *e++=v; fail[v]=chd[fail[u]][i]; // val[v]=(val[v]+val[fail[v]]); } else v=chd[fail[u]][i]; } } } int work(char str[]) { int len=strlen(str); int i,j,k; for(i=0;i<=len;i++) for(j=0;j<sz;j++)dp[i][j]=INF; dp[0][0]=0; int cr,x; for(i=0;i<len;i++) { for(j=0;j<sz;j++) { if(dp[i][j]==INF)continue; for(k=1;k<childnum;k++) { cr=chd[j][k]; if(val[cr])continue; x=dp[i][j]; if(k!=ID[str[i]])x++; dp[i+1][cr]=min(dp[i+1][cr],x); } } } int maxx=INF; for(j=0;j<sz;j++) { maxx=min(maxx,dp[len][j]); } if(maxx==INF)cout<<"-1"<<endl; else cout<<maxx<<endl; } }AC; char tmp[1550]; int main() { AC.init(); int n,m,k,x; int T; T=0; while(~scanf("%d",&n)&&(n)) { T++; AC.reset(); for(int i=1;i<=n;i++) { scanf("%s",tmp); AC.insert(tmp,1); } AC.ac_build(); scanf("%s",tmp); printf("Case %d: ",T); AC.work(tmp); } return 0; }
2,zoj-3228-Searching the String
題目大意:給出一個字符串和若幹個單詞。問這些單詞在字符串裏面出現了多少次。單詞前面為0表示這個單詞可重疊出現。1為不可重疊出現。
做法: 可重疊的非常優點理,就是AC自己主動機處理。不可重疊的就記錄下上次這個單詞出現的位置。假設上次出現的位置加上這個單詞的長度小於等於當前位置的話,那麽這個單詞的次數就加一。否則就不加。
註意:可能存在兩個字符串相等,比較坑爹。。。
#include<stdio.h> #include<algorithm> #include<iostream> #include<stdlib.h> #include<vector> #include<queue> #include<string.h> #include<math.h> using namespace std; #define LL __int64 #define maxn 55 const int maxnode=6*110000; const int childnum=27; const int mod=1000000007; const int INF=99999999; struct ac_tree { int chd[maxnode][childnum]; int val[maxnode][2]; int fail[maxnode]; int last[maxnode]; int num[maxnode][2]; int *ans[maxnode]; int key[1<<11]; int Q[maxnode]; int ID[128]; int sz; void init() { fail[0]=0; for(int i=0;i<childnum;i++) { ID[i+‘a‘]=i+1; } } void reset() { memset(chd,0,sizeof(chd)); memset(num,0,sizeof(num)); sz=1; } void insert(char str[],int k,int tt) { int p=0; int len=strlen(str); for(int i=0;i<len;i++) { int c=ID[str[i]]; if(!chd[p][c]) { memset(chd[sz],0,sizeof(chd[sz])); val[sz][tt]=0; chd[p][c]=sz++; } p=chd[p][c]; } val[p][tt]=len; last[p]=-1; ans[k]=&num[p][tt]; } void ac_build() { int *s=Q,*e=Q; for(int i=1;i<childnum;i++) { if(chd[0][i]) { fail[chd[0][i]]=0; *e++=chd[0][i]; } } while(s!=e) { int u=*s++; //if(val[fail[u]])val[u]+=val[fail[u]]; for(int i=1;i<childnum;i++) { int &v=chd[u][i]; if(v) { *e++=v; fail[v]=chd[fail[u]][i]; // val[v]=(val[v]+val[fail[v]]); } else v=chd[fail[u]][i]; } } } int work(char str[],int n) { int len=strlen(str); int root=0,key,tp,x; for(int i=0;i<len;i++) { tp=ID[str[i]]; root=chd[root][tp]; key=root; while(key!=0) { if(val[key][0]!=0)num[key][0]++; if(val[key][1]!=0) { if(last[key]<=i-val[key][1]) { num[key][1]++; last[key]=i; } } key=fail[key]; } } for(int i=1;i<=n;i++) printf("%d\n",*ans[i]); puts(""); } }AC; char tmp[1550]; char as[110000]; int main() { AC.init(); int n,m,k,x; int T; T=0; while(~scanf("%s",as)) { T++; AC.reset(); scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%d %s",&k,tmp); AC.insert(tmp,i,k); } AC.ac_build(); printf("Case %d\n",T); AC.work(as,n); } return 0; }
3,hdu-3341-Lost‘s revenge
抄的解題報告,感覺說的非常具體。我就不贅余了。
。
題意:給出一些病毒串,一個原串,問怎麽調整原串的順序使得跟最多的病毒串匹配
抄解題報告:
自己主動機+DP。設主串有na個A、nc個C、ng個G、nt個T,於是題意轉化為用na個A、
nc個C、ng個G、nt個T生成一個新串,使模式串匹配次數最大。
先將模式串生成自己主動機
〔trie圖〕,然後在這上面進行DP。狀態可表示為dp[d,s],d為自己主動機狀態。
s表示由ma個A、mc個C、mg個G、mt個T的生成串
〔s可表示為ma*(nc+1)*(ng+1)*(nt+1)+mc*(ng+1)*(nt+1)+mg*(nt+1)+mt〕。
轉移為O(4),總復雜度O(500*11^4*4)
那種表示方法就是變進制吧
直接DP(root,s)會超時
Qinz說這題卡時緊
我用他的那種dfs才幹過。他是先dfs枚舉出狀態,然後對這個狀態遞推計算。好快
比較奇妙,不用管順序的,就記錄個數
#include<stdio.h> #include<algorithm> #include<iostream> #include<stdlib.h> #include<vector> #include<queue> #include<string.h> #include<math.h> using namespace std; #define LL __int64 #define maxn 55 const int maxnode=51*10; const int childnum=5; const int mod=1000000007; const int INF=99999999; struct ac_tree { int chd[maxnode][childnum]; int val[maxnode]; int fail[maxnode]; int Q[maxnode]; int ID[128]; int sz; int dp[maxnode][14642]; void init() { fail[0]=0; for(int i=0;i<childnum;i++) { ID[i+‘a‘]=i+1; } ID[‘A‘]=1;ID[‘T‘]=2; ID[‘G‘]=3;ID[‘C‘]=4; } void reset() { memset(chd,0,sizeof(chd)); memset(val,0,sizeof(val)); sz=1; } void insert(char str[],int k) { // cout<<str<<endl; int p=0; int len=strlen(str); for(int i=0;i<len;i++) { int c=ID[str[i]]; if(!chd[p][c]) { memset(chd[sz],0,sizeof(chd[sz])); val[sz]=0; chd[p][c]=sz++; } p=chd[p][c]; } val[p]+=k; //cout<<p<<" "<<val[p]<<endl; } void ac_build() { int *s=Q,*e=Q; for(int i=1;i<childnum;i++) { if(chd[0][i]) { fail[chd[0][i]]=0; *e++=chd[0][i]; } } while(s!=e) { int u=*s++; if(val[fail[u]])val[u]+=val[fail[u]]; for(int i=1;i<childnum;i++) { int &v=chd[u][i]; if(v) { *e++=v; fail[v]=chd[fail[u]][i]; // val[v]=(val[v]+val[fail[v]]); } else v=chd[fail[u]][i]; } } } int work(char str[]) { int num[5]; memset(num,0,sizeof(num)); int len=strlen(str); for(int i=0;i<len;i++)num[ID[str[i]]]++; dp[0][0]=0; int tai,cr; int sum[6]; sum[5]=1; sum[4]=num[4]+1; sum[3]=(num[3]+1)*sum[4]; sum[2]=(num[2]+1)*sum[3]; sum[1]=(num[1]+1)*sum[2]; int a[5],i,j; int pt; memset(dp,-1,sizeof(dp)); dp[0][0]=0; for(a[1]=0;a[1]<=num[1];a[1]++) for(a[2]=0;a[2]<=num[2];a[2]++) for(a[3]=0;a[3]<=num[3];a[3]++) for(a[4]=0;a[4]<=num[4];a[4]++) { if(a[1]+a[2]+a[3]+a[4]>=len)break; tai=0; for(i=1;i<=4;i++)tai+=a[i]*sum[i+1]; for(i=0;i<sz;i++) { if(dp[i][tai]==-1)continue; for(j=1;j<childnum;j++) { if(a[j]+1>num[j])continue; cr=chd[i][j]; pt=tai+sum[j+1]; // if(dp[cr][pt]==-1)dp[cr][pt]=dp[i][tai]; dp[cr][pt]=max(dp[cr][pt],dp[i][tai]+val[cr]); // cout<<i<<" "<<j<<"->"<<cr<<" "<<pt<<" "<<dp[cr][pt]<<endl; } } } pt=num[1]*sum[2]+num[2]*sum[3]+num[3]*sum[4]+num[4]; int maxx=-1; for(i=0;i<sz;i++) { maxx=max(maxx,dp[i][pt]); } cout<<maxx<<endl; } }AC; char tmp[1550]; char as[110000]; int main() { AC.init(); int n,m,k,x; int T; T=0; while(~scanf("%d",&n)&&n) { T++; AC.reset(); for(int i=1;i<=n;i++) { scanf("%s",tmp); AC.insert(tmp,1); } AC.ac_build(); scanf("%s",as); printf("Case %d: ",T); AC.work(as); } return 0; }
4,hdu-3247-Resource Archiver
題意:
給你 n 個串。和 m 個病毒串。要把這 n 個串
連起來來,能夠重疊,但不能包括 m 個病毒串
中的隨意一個。求把 n 個串連起來的最小長度。
做法:這個題目憋了我非常長時間,一直不知道怎麽樣處理。後來經過翻閱博客。最終悟出來點。
把病毒串和非病毒串都標記在trie樹上處理。然後病毒串的地方標記為病毒串,非病毒串的地方標記為是哪一個串。
狀態壓縮一下。
然後從每一個非病毒串的結尾開始bfs。
map[i][j]:從i字符串的結尾。到j字符串的結尾的最短路徑是多少。
然後就能夠DP了。
dp[i][j]:已用的字符串為i狀態,在trie樹上的點為j狀態。的最短路徑。
#include<stdio.h> #include<algorithm> #include<iostream> #include<stdlib.h> #include<vector> #include<queue> #include<string.h> #include<math.h> using namespace std; #define LL __int64 #define maxn 205 const int maxnode=100001; const int childnum=3; const int mod=1000000007; const int INF=99999999; struct list { int st; int x; }pp,qq; struct ac_tree { int chd[maxnode][childnum]; int val[maxnode]; int fail[maxnode]; int Q[maxnode]; int var[maxnode]; int vis[maxnode]; int dist[maxnode]; int st[maxn]; int ID[128]; int sz; int maps[maxn][maxn]; int ns; int dp[1<<11][maxn]; void init() { fail[0]=0; for(int i=0;i<childnum;i++) { ID[i+‘a‘]=i+1; } ID[‘0‘]=1;ID[‘1‘]=2; } void reset() { memset(chd,0,sizeof(chd)); memset(val,0,sizeof(val)); memset(var,0,sizeof(var)); memset(maps,-1,sizeof(maps)); sz=1; } void insert(char str[],int k,int ps) { // cout<<str<<endl; int p=0; int len=strlen(str); for(int i=0;i<len;i++) { int c=ID[str[i]]; if(!chd[p][c]) { memset(chd[sz],0,sizeof(chd[sz])); val[sz]=0; chd[p][c]=sz++; } p=chd[p][c]; } if(ps==0) { val[p]=k; var[p]=0; } else var[p]=1; //cout<<p<<" "<<val[p]<<endl; } void ac_build() { int *s=Q,*e=Q; for(int i=1;i<childnum;i++) { if(chd[0][i]) { fail[chd[0][i]]=0; *e++=chd[0][i]; } } while(s!=e) { int u=*s++; //if(val[fail[u]]==-1)val[u]=-1; for(int i=1;i<childnum;i++) { int &v=chd[u][i]; if(v) { *e++=v; fail[v]=chd[fail[u]][i]; val[v]=(val[v]|val[fail[v]]); var[v]=(var[v]|var[fail[v]]); } else v=chd[fail[u]][i]; } } } int bfs(int u) { queue<int>que; while(!que.empty())que.pop(); int x=st[u]; que.push(x); memset(dist,-1,sizeof(dist)); dist[x]=0; while(!que.empty()) { x=que.front(); que.pop(); for(int i=1;i<childnum;i++) { int y=chd[x][i]; if(var[y])continue; if(dist[y]<0) { dist[y]=dist[x]+1; que.push(y); } } } for(int i=0;i<ns;i++) { maps[u][i]=dist[st[i]]; //cout<<u<<"->"<<i<<"="<<maps[u][i]<<endl; } } int work(int n) { st[0]=0; ns=1; for(int i=0;i<sz;i++) if(val[i]) { st[ns++]=i; //cout<<ns-1<<" "<<val[st[ns-1]]<<endl; } for(int i=0;i<ns;i++)bfs(i); memset(dp,-1,sizeof(dp)); dp[0][0]=0; int cr; for(int i=0;i<(1<<n);i++) { for(int j=0;j<ns;j++) { if(dp[i][j]<0)continue; for(int k=0;k<ns;k++) { if(maps[j][k]<0)continue; cr=i|val[st[k]]; if(dp[cr][k]==-1)dp[cr][k]=dp[i][j]+maps[j][k]; else dp[cr][k]=min(dp[cr][k],dp[i][j]+maps[j][k]); // cout<<cr<<" "<<k<<" "<<dp[cr][k]<<endl; } } } int minn=-1; cr=(1<<n)-1; for(int i=0;i<ns;i++) { if(dp[cr][i]==-1)continue; if(minn<0)minn=dp[cr][i]; minn=min(minn,dp[cr][i]); } cout<<minn<<endl; } }AC; char tmp[1550]; int main() { AC.init(); int n,m,k,x; int T; T=0; while(~scanf("%d%d",&n,&m)&&(n||m)) { AC.reset(); for(int i=1;i<=n;i++) { scanf("%s",tmp); AC.insert(tmp,1<<(i-1),0); } for(int i=1;i<=m;i++) { scanf("%s",tmp); AC.insert(tmp,1,1); } AC.ac_build(); AC.work(n); } return 0; }
每日總結-05-19(AC自己主動機結束)