HihoCoder 后缀自动机入门1-6题解
比起后缀数组,我觉得后缀自动机比较好理解也。。。
#1441 : 后缀自动机一·基本概念
endpos集合相同的子串才是一个同一个状态。暴力模拟即可。
#include<bits/stdc++.h> #include<tr1/unordered_map> using namespace std; typedef long long ll; tr1::unordered_map<string,ll> mmp; tr1::unordered_map<ll,string> shortest,longest; int main(){ string s,ss; cin>>s; int n,lens=s.size(); ll state; for(int i=0;i<lens;i++) for(int j=1;j<=lens-i;j++){ state=0; ss=s.substr(i,j); for(int k=0;k<=lens-j;k++){ if(ss==s.substr(k,j)) state|=(1ll<<(k+j-1)); } if(!shortest.count(state)||shortest[state].size()>j) shortest[state]=ss; if(!longest.count(state)||longest[state].size()<j) longest[state]=ss; mmp[ss]=state; } cin>>n; while(n--){ cin>>ss; state=mmp[ss]; cout<<shortest[state]<<" "<<longest[state]; for(int i=0;i<lens;i++){ if(state&(1ll<<i)) printf(" %d",i+1); } printf(" "); } return 0; }
#1445 : 后缀自动机二·重复旋律5
求不同子串的个数,而每个状态中len[i]为这个状态的最长子串长度,而它最短子串长度为len[link[i]]+1(因为它的后缀是在link[i]处断的嘛)
所以每个状态最长子串长度减去最短子串长度+1就是这个状态有多少种不同子串,然后全部加起来即可。
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N=2e6+11; char s[N]; int size,last,maxlen[N];//minlen[N]; //拥有相同endpos集合的为同一状态 //对于同一状态中的字符串,他们都是该状态最长子串的后缀 //size总状态数,last上一个状态编号,maxlen[i]:i状态包含的最长子串长度 int link[N],trans[N][31]; //trans[i][j] 转移函数,为i状态遇到j字符会转移到哪个状态 //link[i] SuffixLinks,i状态的连续后缀在哪个状态断开 void initsam(int n){ size=last=1; for(int i=0;i<=n;i++){ link[i]=maxlen[i]=0;//minlen[i]=0; for(int j=0;j<26;j++) trans[i][j]=0; } } void extend(int x){ int cur=++size,u; maxlen[cur]=maxlen[last]+1; //Suffixpath(cur-S)路径上没有对x的转移的状态,添加到cur的转移 for(u=last;u&&!trans[u][x];u=link[u]) trans[u][x]=cur; //若Suffixpath(cur-S)路径上的状态都没有对x的转移,那么此时curlink到初状态即可 if(!u) link[cur]=1; else{ //若Suffixpath(cur-S)路径存在有对x转移的状态u //而v是u遇到x后转移到的状态 int v=trans[u][x]; //若v中最长的子串添加上x便是u的最长子串,此时将curlink到v //也就是v状态中的子串都是cur状态中的后缀,且cur的后缀序列刚好在v处断开 if(maxlen[v]==maxlen[u]+1) link[cur]=v; else{ //否则创建个中间状态进行转移 //也就是cur状态和v状态都有着部分相同的后缀,而之前这些后缀保存在v状态 //而v状态中还有些状态不是cur状态的后缀的,所以需要个新状态表示他们共有的后缀 int clone=++size; maxlen[clone]=maxlen[u]+1; memcpy(trans[clone],trans[v],sizeof(trans[v])); link[clone]=link[v]; // minlen[clone]=maxlen[link[clone]]+1; //原先添加x后转移到v的状态,现在都转移到中间状态 for(;u&&trans[u][x]==v;u=link[u]) trans[u][x]=clone; //最后,因为cur状态和v状态的后缀都在中间状态这里断开 //所以cur和v都link到中间状态 link[cur]=link[v]=clone; // minlen[v]=maxlen[link[v]]+1; } } // minlen[cur]=maxlen[link[cur]]+1; last=cur; return ; } int main(){ scanf("%s",s); int lens=strlen(s); initsam(2*lens); for(int i=0;i<lens;i++) extend(s[i]-'a'); ll ans=0; for(int i=2;i<=size;i++) ans+=maxlen[i]-maxlen[link[i]]; printf("%lld ",ans); return 0; }