求长度不超过l的串中,包含一些单词的串的个数
首先构造自动机,然后把每个包含了终结态的状态都置为终结态,比如abc b,那么ab,abc,b都是终结态,
然后构造矩阵A,Ax就是长度为一时得到的结果A^n为长度为n结果
其中x=[0,1,.........0]第一个表示终结状态,第二个表示起始状态,最后一个表示为长度小于当前长度的终结态数量之和(终结态表示这个串包含了至少一个单词)
接着构造A
对自动机上任意一个状态转移u->v如果u是终结态就不转移,否则A[v][u]++,
然后就用矩阵快速幂来求解A^n
最后的答案就是A[0][1]+A[s][1] ==== 因为只有x[1] =1其他=0,表示当前长度终结态数量+小于当前长度终结态数量 = 最后答案
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; #define maxn 40 int child[maxn][26],fail[maxn],state[maxn],flag[maxn]; int cnt; void init(){ memset(child,0,sizeof(child)); memset(flag, 0,sizeof(flag)); memset(fail, 0,sizeof(fail)); cnt = 2; for(int i = 0;i < 26;i++) child[0][i] = 1; } char word[maxn]; void add(int u,int i){ int x = word[i]-'a'; if(word[i]=='\0'){ flag[u] = 1; return ; } if(child[u][x]==0)child[u][x] = cnt++; add(child[u][x],i+1); } #define ll unsigned __int64 int queue[100]; void build(){ int front=0,rear=0; queue[front++] = 1; while(front != rear){ int u = queue[rear++]; flag[u] |= flag[fail[u]]; for(int i = 0;i < 26;i++){ if(child[u][i]==0) child[u][i] = child[fail[u]][i]; else { int v = child[u][i]; fail[v] = child[fail[u]][i]; queue[front++] = v; } } } } struct Matrix{ ll res[30][30]; ll n; void init(){ memset(res,0,sizeof(res)); } }; Matrix operator*(Matrix a,Matrix b){ Matrix c; c.init(); c.n = a.n; for(int i = 0;i < a.n;i++){ for(int j = 0;j < a.n ;j++) for(int k = 0;k < a.n ;k++) c.res[i][j]+=a.res[i][k]*b.res[k][j]; } return c; } int main(){ ll n,l; while(cin>>n>>l){ init(); for(int i = 0;i < n;i++){ cin>>word; add(1,0); } build(); int s = 1; for(int i = 1;i < cnt; i++) if(flag[i]==0)state[i]=s++; else state[i] = 0; Matrix a; a.init(); for(int i = 1;i < cnt; i++){ for(int j = 0;j < 26;j++){ int u = state[child[i][j]]; int v = state[i]; if(v == 0 ) continue; a.res[u][v]++; } } a.res[0][0] = 26; a.res[s][0] = 1; a.res[s][s] = 1; Matrix ans; ans.init(); ans.n = a.n = s+1; for(int i = 0;i < 30;i++) ans.res[i][i] = 1; while(l){ if(l&1) ans=a*ans; a=a*a; l/=2; } ll f = ans.res[s][1]+ans.res[0][1]; printf("%I64u\n",f); } return 0; }
求一个串包含哪些字串,根据题目意思的话,就不用想太多了,不会有重叠情况的,
比如abc bc这种的。
解题:把字串建立ac自动机,是单词的终结态就标记为该单词的标号,
然后对于每个串走一遍自动机,如果这个标号没有出现过就加入ans
由于ans的大小<3 就直接判定就行了
#include<iostream> #include<cstring> #include<cstdio> #include<algorithm> #include<vector> #include<set> using namespace std; #define maxn 100007 struct Node{ int child[128]; int flag; Node*fail; }; Node node[maxn]; int cnt; int newNode(){ memset(node[cnt].child,-1,sizeof(node[cnt].child)); node[cnt].flag = 0; return cnt++; } vector<int>ans; char word[maxn]; int super,root; void insert(int u,int p,int flag){ if(word[p] == '\0'){ node[u].flag = flag; return ; } int l = word[p]; if(node[u].child[l] == -1){ node[u].child[l] = newNode(); } insert(node[u].child[l],p+1,flag); } int queue[maxn]; void build(){ for(int i = 0;i < 128;i++) node[super].child[i] = root; node[root].fail = &node[super]; int front = 0,rear=0; queue[front++] = root; while(front > rear){ int u = queue[rear++]; for(int i = 0;i < 128;i++){ if(node[u].child[i] == -1){ node[u].child[i] = node[u].fail->child[i]; } else { node[node[u].child[i]].fail = &node[node[u].fail->child[i]]; queue[front++] = node[u].child[i]; } } } } void find(){ int len=strlen(word); int u = root; for(int i = 0;i < len; i++){ u = node[u].child[word[i]]; if(node[u].flag != 0){ int flag = 1; for(int i = 0;i < ans.size() ;i++) if(ans[i] == node[u].flag) flag = 0; if(flag == 1) ans.push_back(node[u].flag); } if(ans.size() == 4)return ; } } int main(){ int total,n; while(scanf("%d",&n)!=EOF){ cnt = 0; super = newNode(); root = newNode(); for(int i = 1;i <= n;i++){ scanf("%s",word); insert(root,0,i); } build(); scanf("%d",&n); int total = 0; for(int i = 1;i <= n;i++){ scanf("%s",word); ans.clear(); ans.push_back(0); find(); if(ans.size() > 1){ sort(ans.begin(),ans.end()); printf("web %d:",i); for(int j = 1;j < ans.size();j++) printf(" %d",ans[j]); printf("\n"); total++; } } printf("total: %d\n",total); } return 0; }
解法:构建ac自动机是单词结尾的标记一下
然后走ac自动机,走到一个状态访问fail节点,如果mark!=-就加上mark,然后mark=-1
这样经过的状态就不会重复计算了,
#include<iostream> #include<cstdio> #include<queue> #include<cstring> using namespace std; struct Node{ Node*ch[26],*fail; int mark,id; }word[600001]; int cnt; Node*newNode(){ memset(&word[cnt],0,sizeof(word[0])); word[cnt].id = cnt; return &word[cnt++]; } void insert(Node*root,char*s){ if(*s==0){root->mark++;return;} int id = *s-'a'; if(root->ch[id] == 0) root->ch[id] = newNode(); insert(root->ch[id],s+1); } Node *super,*root; void build(Node*root){ for(int i = 0;i < 26;i++) super->ch[i] = root; root->fail = super; queue<Node*>Q; Q.push(root); while(!Q.empty()){ Node* q = Q.front(); Q.pop(); for(int i = 0;i < 26; i++){ if(q->ch[i] != NULL){ Q.push(q->ch[i]); q->ch[i]->fail = q->fail->ch[i]; //cout<<(char)(i+'a')<<endl; } else q->ch[i] = q->fail->ch[i]; } } } int autofind(char *s){ int ans = 0,id; Node*rt = root,*p; while(*s){ id = *s - 'a'; if(rt->ch[id] != root) rt = rt->ch[id]; else{ while(rt != root && rt->ch[id] == root) rt = rt->fail; } p = rt; while(p != root && p->mark != -1){ ans += p->mark; p->mark = -1; p = p->fail; } s++; } return ans; } char a[1000009]; int main(){ int t,n; scanf("%d",&t); while(t--){ cnt = 0; super = newNode(); root = newNode(); scanf("%d",&n); while(n--){ scanf("%s",a); insert(root,a); } build(root); scanf("%s",a); int ans = autofind(a); printf("%d\n",ans); } return 0; }