参考题解
题目大意:给一个字符串S和一系列字符串T1~Tn,问在S中有多少个不同子串满足它不是T1~Tn中任意一个字符串的子串。
思路:我们先构造S的后缀自动机,然后将每一个Ti在S的SAM上做匹配,类似于LCS,在S中的每一个状态记录一个变量deep,表示T1~Tn,在该状态能匹配的最大长度是多少,将每一个Ti匹配完之后,我们将S的SAM做拓扑排序,自底向上更新每个状态的deep,同时计算在该状态上有多少个子串满足题目要求。具体步骤如下:
1:对于当前状态,设为p,设p的par为q,则更新q->deep为q->deep和p->deep中的较大值。
2:若p->deep<p->val,则表示在状态p中,长度为p->deep+1~p->val的子串不是T1~Tn中任意字符串的子串,所以答案加上p->val-p->deep。否则表示状态p中所有字串均不满足要求,跳过即可。
(注意若p->deep==0,表示状态p中所有的子串均满足题目要求,但是答案不是加上p->val-0,而是加上 p->val-p->par->val,这表示状态p中的字符串个数,所以对于p->deep==0要特殊处理)
最后输出答案即可。
3 2 abab ab ba 1 aaa bbb 2 aaaa aa aaa
Case 1: 3 Case 2: 3 Case 3: 1
#include<iostream> #include<cstring> #include<cstdio> using namespace std; #define prt(k) cout<<#k"="<<k<<endl; #define ll long long const int N=200200; struct Node { Node *ch[26],*f; int len,id,pos; Node() {} Node(int _len) { len=_len; memset(ch,0,sizeof ch); f=0; } }sam[N],*root,*last; int tot; ///SAM_size Node* newnode(int len) { sam[tot]=Node(len); sam[tot].id=tot; return &sam[tot++]; } Node* newnode(Node* p) { sam[tot]=*p; sam[tot].id=tot; return &sam[tot++]; } void SAM_init() { tot=0; root=last=newnode(0); sam[0].pos=0; } #define node Node void add(int x,int len) { Node *p=last,*np=newnode(p->len+1); np->pos=len; last=np; while(p&&!p->ch[x]) p->ch[x]=np,p=p->f; if(!p) { np->f=root;return; } node *q=p->ch[x]; if(q->len==p->len+1) { np->f=q; return; } ///!!! node* nq=newnode(q); nq->len=p->len+1; q->f=nq; np->f=nq; for(;p&&p->ch[x]==q;p=p->f) p->ch[x]=nq; } void SAM_build(char s[]) { SAM_init(); for(int i=0;s[i];i++) add(s[i]-'a',i+1); } Node* top[N]; ///toposort char s[N]; int c[N]; int dp[N]; void Max(int& a,int b) { if(a<b) a=b; } int main() { int re; cin>>re; int ca=1; while(re--) { int m; cin>>m; scanf("%s",s); SAM_build(s); memset(c,0,sizeof c); memset(dp,0,sizeof dp); memset(top,0,sizeof top); for(int i=0;i<tot;i++) c[sam[i].len]++; for(int i=1;i<=tot;i++) c[i]+=c[i-1]; for(int i=0;i<tot;i++) top[--c[sam[i].len]]=&sam[i]; while(m--) { node* p=root; scanf("%s",s); int len=strlen(s); int tmp=0; for(int i=0;i<len;i++) { int x=s[i]-'a'; if(p->ch[x]) { tmp++; p=p->ch[x]; Max(dp[p->id],tmp); } else { while(p&&!p->ch[x])p=p->f; if(p) { tmp=p->len+1; p=p->ch[x]; Max(dp[p->id],tmp); } else { tmp=0; p=root; } } } } ll ans=0; for(int i=tot-1;i>0;i--) { Node *p=top[i]; if(dp[p->id]==0) { ans+=p->len-p->f->len; continue; } if(p->f) Max(dp[p->f->id],dp[p->id]); if(dp[p->id]<p->len) ans+=p->len-dp[p->id]; } printf("Case %d: %I64d\n",ca++,ans); } }