@xiaoziyao
        
        2020-08-08T15:15:38.000000Z
        字数 2352
        阅读 1489
    解题报告
题意:给定一个主串和个询问串,对于每个询问串,需要求出它所有本质不同循环同构在中出现的次数之和。
数据范围:,,
一道SAM好题,考察了对DAWG和Parent Tree的理解。
解释循环同构的定义:给定字符串(每个是字符),它的循环同构为:,,,,(比如说,的循环同构有,,)。
考虑循环同构的实质,其实是在前面去除一个字符,并在后面添加一个字符。
在前面去除一个字符?根据endpos和后缀link的性质,一个属于状态的串在前面去除一个字符要么还在这个状态,要么跳后缀link到。
在后面添加一个字符?根据DAWG边的定义(其实就是后缀自动机里的边),一个属于状态的串在后面添加字符会跳转到状态。
这就比较好办了,我们输入后复制一遍它放在它后面(注意不要复制最后一个字符),这样形成的就会包含的所有循环同构。
然后循环每个字符进行匹配,按照上面的方式跳后缀link和DAWG边,并维护长度,如果长度就记录答案(意味着整个子串都成功匹配),注意记录完答案后需要强制失配,这样就不会让长度(因为的所有循环同构长度都等于,所以如果一定是倍长的锅,比如,,倍长后,这会让匹配的长度大于)。
至于本质不同,其实很好处理。我们在每一次记录答案的时候可以作一个标记(我使用的是时间戳标记,这样不需要给数组清空),标记后的结点不对答案做贡献就可以了。
#include<stdio.h>const int maxn=2000005,maxk=30,maxm=2000005,maxt=2000005;int i,j,k,m,n,T,tot,last,e,stp,cnt;int len[maxn],link[maxn],nxt[maxn][maxk],start[maxn],to[maxm],then[maxm],tag[maxn],size[maxn],vis[maxn],s[maxn],t[maxn];char c;inline void add(int x,int y){then[++e]=start[x],start[x]=e,to[e]=y;}int extend(int last,int x){int pos=last,now=++tot,tmp,cln;tag[now]=1;len[now]=len[pos]+1;while(pos!=0&&nxt[pos][x]==0)nxt[pos][x]=now,pos=link[pos];if(pos==0){link[now]=1;return now;}tmp=nxt[pos][x];if(len[tmp]==len[pos]+1){link[now]=tmp;return now;}cln=++tot;len[cln]=len[pos]+1;for(int i=1;i<=26;i++)nxt[cln][i]=nxt[tmp][i];link[cln]=link[tmp],link[tmp]=link[now]=cln;while(pos!=0&&nxt[pos][x]==tmp)nxt[pos][x]=cln,pos=link[pos];return now;}void dfs(int x){size[x]=tag[x];for(int i=start[x];i;i=then[i]){int y=to[i];dfs(y);size[x]+=size[y];}}int main(){for(c=getchar();c<'a'||c>'z';c=getchar());for(;c>='a'&&c<='z';c=getchar())s[++cnt]=c-96;last=tot=1;for(i=1;i<=cnt;i++)last=extend(last,s[i]);for(i=2;i<=tot;i++)add(link[i],i);dfs(1);scanf("%d",&T);while(T--){int now=1,l=0,ans=0;cnt=0,stp++;for(c=getchar();c<'a'||c>'z';c=getchar());for(;c>='a'&&c<='z';c=getchar())t[++cnt]=c-96;for(i=cnt+1;i<2*cnt;i++)t[i]=t[i-cnt];for(i=1;i<2*cnt;i++){while(now!=0&&nxt[now][t[i]]==0)now=link[now],l=len[now];if(nxt[now][t[i]])l++,now=nxt[now][t[i]];else l=0,now=1;if(l==cnt){if(vis[now]!=stp)vis[now]=stp,ans+=size[now];if(len[link[now]]+1==cnt)now=link[now];l--;}}printf("%d\n",ans);}return 0;}
