Gym 101194F Mr. Panda and Fantastic Beasts

xiaoxiao2021-02-28  70

Gym 101194F Mr. Panda and Fantastic Beasts

2016acm-icpc 中国赛区总决赛 F题 后缀自动机

题意

给n个字符串,找第一个字符串的一个最短子串,使得它不是2到n任何一个字符串的子串。输出字典序最小的。

思路

SAM。算半个裸题? %%%

把2到n用特殊字符连接起来(‘z’+1),拼成一个字符,构建sam。然后用第一个字符串跟他匹配,每次失配时更新答案。

sam的每个状态代表一系列子串,我们需要取一个最小的。留下minlen数组也可以,如果不留下minlen数组,那么每个状态的最小长度就是沿着失配边走第一个状态的最大长度加1。每次失配更新答案长度与字典序,然后沿着失配边走,这个过程就是缩小目前可匹配的子串的过程,类似kmp。沿着失配边走不了的时候,要么是可以接着匹配了,要么是走到起点了,开始下一轮的匹配。

代码

注意长度和字典序的写法。。因为zz错误对拍半天。。。唉

#include <iostream> #include <algorithm> #include <cstdio> #include <cstring> #include <cstdlib> #include <cmath> #include <ctime> #include <vector> #include <queue> #include <stack> #include <deque> #include <string> #include <map> #include <set> #include <list> #define M(a,b) memset(a,b,sizeof(a)) typedef long long LL; using namespace std; const int MAXL=1000005; const int MAXS=27; const int oo=0x3f3f3f3f; struct SAM { int n=0, st; //maxlen表示每个状态对应的子串中长度最大值,minlen最小值 //trans是转移边,根节点是1,所以0代表不存在 //slink表示绿色的suffix link //col表示节点颜色,col=1代表是后缀节点,=0代表不是后缀的节点 //indeg表示对于只含有suffix link的图的度,统计endposamu用 //endposa表示该状态对应的子串们出现的结尾位置,amu表示endpos集合的大小,也表示该状态对应的子串的重复次数 //ans表示某一长度的子串的个数 int maxlen[2*MAXL+10], trans[2*MAXL+10][MAXS], slink[2*MAXL+10]; int new_state(int _maxlen, int* _trans, int _slink) { n++; maxlen[n]=_maxlen; for(int i=0; i<MAXS; i++) { if(_trans==NULL) trans[n][i]=0; else trans[n][i]=_trans[i]; } slink[n]=_slink; return n; } int add_char(char ch, int u) { int c=ch-'a'; int z=new_state(maxlen[u]+1, NULL, 0); int v=u; while(v!=0&&trans[v][c]==0) { trans[v][c]=z; v=slink[v]; } if(v==0)//最简单的情况,suffix-path(u->S)上都没有对应字符ch的转移 { slink[z]=1; return z; } int x=trans[v][c]; if(maxlen[v]+1==maxlen[x])//较简单的情况,不用拆分x { slink[z]=x; return z; } int y=new_state(maxlen[v]+1, trans[x], slink[x]); //最复杂的情况,拆分x slink[x]=y;slink[z]=y; int w=v; while(w!=0&&trans[w][c]==x) { trans[w][c]=y; w=slink[w]; } return z; } void init() { n=0; memset(maxlen, 0, sizeof(maxlen)); memset(trans, 0, sizeof(maxlen)); memset(slink, 0, sizeof(maxlen)); st=new_state(0, NULL, 0); } void addstring(string s) { int la=st; for(auto tmp:s) { la=add_char(tmp, la); } } }sam; pair<int,int> match(string s) { int now=sam.st, l=0; int ans=oo;int lp=0, rp=oo; for(int i=0;i<s.size();i++) { int x=s[i]-'a'; if(sam.trans[now][x])// now=sam.trans[now][x], l=sam.maxlen[sam.slink[now]]+1; else { while(now&&!sam.trans[now][x]) { if(l+1<ans) { ans=l+1; lp=i-l;rp=i; } else if(l+1==ans) { for(int j=1;j<=l+1;j++) { if(s[i-l+j-1]<s[lp+j-1]) { lp=i-l;rp=i; break; } else if(s[i-l+j-1]>s[lp+j-1]) break; } } now=sam.slink[now], l=sam.maxlen[sam.slink[now]]+1; } if(!now) now=sam.st, l=0; else now=sam.trans[now][x], l=sam.maxlen[sam.slink[now]]+1; } } return make_pair(lp, rp); } int main() { int T;scanf("%d", &T);int cas=0; while(T--) { sam.init(); int n;scanf("%d", &n); string s0;cin>>s0; string s, s1;cin>>s; for(int i=2;i<n;i++) { cin>>s1; s+=('z'+1); s+=s1; } sam.addstring(s); pair<int, int> p=match(s0); printf("Case #%d: ", ++cas); if(p.second>s.length()) printf("Impossible\n"); else { for(int i=p.first;i<=p.second;i++) { printf("%c", s0[i]); } printf("\n"); } } return 0; }
转载请注明原文地址: https://www.6miu.com/read-97220.html

最新回复(0)