hihocoder #1465 : 后缀自动机五·重复旋律8
时间限制:10000ms
单点时限:1000ms
内存限制:256MB
描述
小Hi平时的一大兴趣爱好就是演奏钢琴。我们知道一段音乐旋律可以被表示为一段数构成的数列。
小Hi发现旋律可以循环,每次把一段旋律里面最前面一个音换到最后面就成为了原旋律的“循环相似旋律”,还可以对“循环相似旋律”进行相同的变换能继续得到原串的“循环相似旋律”。
小Hi对此产生了浓厚的兴趣,他有若干段旋律,和一部音乐作品。对于每一段旋律,他想知道有多少在音乐作品中的子串(重复便多次计)和该旋律是“循环相似旋律”。
解题方法提示
输入
第一行,一个由小写字母构成的字符串S,表示一部音乐作品。字符串S长度不超过100000。
第二行,一个整数N,表示有N段旋律。接下来N行,每行包含一个由小写字母构成的字符串str,表示一段旋律。所有旋律的长度和不超过 100000。
输出
输出共N行,每行一个整数,表示答案。
样例输入
abac
3
a
ab
ca
样例输出
2
2
1
思路: 这个题目,由于要考虑循环相似,所以我们对所有的模式串进行加倍处理,即添加相同的串到原串的最后。
先对主串建立sam,然后模式串去主串上匹配,匹配的过程类似于spoj里面lcs和lcs2的做法,如果有这个字符的儿子,就沿着这个边走下去,否则就跳转到他的父亲,直到某个点拥有当前这个字符作为儿子,然后我们就走到这个儿子上去。
每次统计当前匹配的一个最大长度,如果这个最大长度超过或者等原来模式串的长度,那么我们就不断向上沿着fa找到第一个a[i].len>=模式串长度,这样保证模式串一定在这个节点所代表的集合里面出现,然后答案累积上这个sz。
注意,由于会找到相同的节点,而每个节点在一个模式串匹配中也只能被用1次,所以我们需要标记已经用过的节点,用vis数组标记。
1 #include<bits/stdc++.h> 2 using namespace std; 3 int const N=100000+3; 4 struct node{ 5 int len,fa,ch[26]; 6 }a[N<<2]; 7 int n,tot,ls,sz[N<<1],num[N],sa[N<<1],vis[N<<1]; 8 vector<int> d; 9 char s[N]; 10 void add(int c,int id){ 11 int p=ls; 12 int np=ls=++tot; 13 a[np].len=a[p].len+1; 14 sz[np]=1; 15 for(;p&&!a[p].ch[c];p=a[p].fa) a[p].ch[c]=np; 16 if(!p) a[np].fa=1; 17 else { 18 int q=a[p].ch[c]; 19 if(a[q].len==a[p].len+1) a[np].fa=q; 20 else { 21 int nq=++tot;a[nq]=a[q]; 22 a[nq].len=a[p].len+1; 23 a[q].fa=a[np].fa=nq; 24 for(;p&& a[p].ch[c]==q;p=a[p].fa) 25 a[p].ch[c]=nq; 26 } 27 } 28 } 29 void solve(char *s){ 30 int len=strlen(s),ans=0,p=1,tmp=0; 31 d.clear(); 32 for(int i=0;i<2*len;i++) { 33 int c=s[i%len]-'a'; 34 if(a[p].ch[c]) tmp++,p=a[p].ch[c]; 35 else { 36 while (p && !a[p].ch[c]) p=a[p].fa; 37 if(!p) tmp=0,p=1; 38 else { 39 tmp=a[p].len+1; 40 p=a[p].ch[c]; 41 } 42 } 43 if(tmp>=len){ 44 int x=p,t=a[p].fa; 45 while (t && a[t].len>=len) 46 x=t,t=a[t].fa; 47 if(!vis[x]) ans+=sz[x],d.push_back(x); 48 vis[x]=1; 49 } 50 } 51 printf("%d\n",ans); 52 for(int i=0;i<d.size();i++) vis[d[i]]=0; 53 } 54 int main(){ 55 tot=ls=1; 56 scanf("%s",s); 57 int len=strlen(s); 58 for(int i=0;s[i];i++) 59 add(s[i]-'a',i+1); 60 for(int i=1;i<=tot;i++) num[a[i].len]++; 61 for(int i=1;i<=len;i++) num[i]+=num[i-1]; 62 for(int i=1;i<=tot;i++) sa[num[a[i].len]--]=i; 63 for(int i=tot;i>=1;i--){ 64 int x=sa[i]; 65 int f=a[x].fa; 66 sz[f]+=sz[x]; 67 } 68 scanf("%d",&n); 69 while (n--){ 70 scanf("%s",s); 71 solve(s); 72 } 73 return 0; 74 }