SPOJ1812 Longest Common Substring II
題意
A string is finite sequence of characters over a non-empty finite set Σ.
In this problem, Σ is the set of lowercase letters.
Substring, also called factor, is a consecutive sequence of characters occurrences at least once in a string.
Now your task is a bit harder, for some given strings, find the length of the longest common substring of them.
Here common substring means a substring of two or more strings.
給定n個串,求它們的最長公共子串。
at most 10 lines,no more than 100000
分析
參照iwt的題解。
本題容易看出就是分別將所有串的所有匹配長度記錄在狀態上,然後取所有串記錄值的min,後再對所有狀態取max。
但是不要忘記了一點:更新parent樹的祖先。
為什麽呢?首先如果子樹被匹配過了,那麽長度一定大於任意祖先匹配的長度(甚至有些祖先匹配長度為0!為什麽呢,因為我們在匹配的過程中,只是找到一個子串,可能還遺漏了祖先沒有匹配到,這樣導致了祖先的記錄值為0,那麽在對對應狀態取min的時候會取到0,這樣就wa了。而且註意,如果匹配到了當前節點,那麽祖先們一定都可以賦值為祖先的length!因為當前節點的length大於任意祖先。(
比如數據
acbbc
bc
ac
答案應該是1沒錯吧。如果沒有更新祖先,那麽答案會成0。
這個多想想就行了。
所以以後記住:對任意多串匹配時,凡是對同一個狀態取值時,要註意當前狀態的子樹是否比當前狀態記錄的值優。
時間復雜度:線性。
代碼
#include<bits/stdc++.h> #define rg register #define il inline #define co const template<typename T>il T read() { rg T data=0,w=1; rg char ch=getchar(); while(!isdigit(ch)) { if(ch=='-') w=-1; ch=getchar(); } while(isdigit(ch)) { data=data*10+ch-'0'; ch=getchar(); } return data*w; } template<typename T>il T read(rg T&x) { return x=read<T>(); } typedef long long ll; co int N=2e5; namespace SAM { int tot,last; int ch[N][26],fail[N]={-1},len[N]; void extend(int k) { int cur=++tot; len[cur]=len[last]+1; int p=last; while(~p&&!ch[p][k]) { ch[p][k]=cur; p=fail[p]; } if(p==-1) fail[cur]=0; else { int q=ch[p][k]; if(len[q]==len[p]+1) fail[cur]=q; else { int clone=++tot; std::copy(ch[q],ch[q]+26,ch[clone]); fail[clone]=fail[q],len[clone]=len[p]+1; while(~p&&ch[p][k]==q) { ch[p][k]=clone; p=fail[p]; } fail[cur]=fail[q]=clone; } } last=cur; } int c[N],id[N],mx[N],arr[N]; void build(char s[],int n) { for(int i=0;i<n;++i) extend(s[i]-'a'); for(int i=0;i<=tot;++i) ++c[len[i]]; for(int i=1;i<=n;++i) c[i]+=c[i-1]; for(int i=0;i<=tot;++i) id[--c[len[i]]]=i; // edit 1:--c for 0 std::copy(len,len+tot+1,mx); } void find(char s[],int n) { int p=0,l=0; for(int i=0;i<n;++i) { int k=s[i]-'a'; if(ch[p][k]) p=ch[p][k],++l; else { while(~p&&!ch[p][k]) p=fail[p]; if(p==-1) p=l=0; else l=len[p]+1,p=ch[p][k]; } arr[p]=std::max(arr[p],l); } for(int i=tot;i>=0;--i) { int p=id[i]; mx[p]=std::min(mx[p],arr[p]); if(arr[p]&&fail[p]) arr[fail[p]]=len[fail[p]]; arr[p]=0; } } int getans() { int ans=0; for(int i=0;i<=tot;++i) ans=std::max(ans,mx[i]); return ans; } } char buf[N]; int main() { // freopen(".in","r",stdin); // freopen(".out","w",stdout); scanf("%s",buf); SAM::build(buf,strlen(buf)); while(~scanf("%s",buf)) SAM::find(buf,strlen(buf)); printf("%d\n",SAM::getans()); return 0; }
SPOJ1812 Longest Common Substring II