hihocoder #1465 : 后缀自动机五·重复旋律8

Posted zjxxcn

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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

 

思路:  这个题目,由于要考虑循环相似,所以我们对所有的模式串进行加倍处理,即添加相同的串到原串的最后。 

先对主串建立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  
View Code

 

以上是关于hihocoder #1465 : 后缀自动机五·重复旋律8的主要内容,如果未能解决你的问题,请参考以下文章

hihocoder #1465 : 后缀自动机五·重复旋律8

hihocoder 1465 循环串匹配问题(后缀自动机)

hihocoder 后缀自动机五·重复旋律8 求循环同构串出现的次数

[后缀自动机] hihoCoder 1145

hihoCoder #1457 : 后缀自动机四·重复旋律7(后缀自动机 + 拓扑排序)

后缀自动机专题(hihocoder)