一.LCS(1e6)
最基础的SAM题,建第一个串的SAM,然后把第二个串放在上面跑的时候如果跑不动就回到root,不然就走下去。
Code:
#include<bits/stdc++.h>
const int N=2000005;
typedef long long ll;
using namespace std;
char s[N],t[N];
int n,len=0,ans=0;
struct SuffixAutoMaton{
int last,tot;int ch[N<<1][26],fa[N<<1],l[N<<1];
void ins(int c){
int p=last,np=++tot;last=np;l[np]=l[p]+1;
for(;p&&!ch[p][c];p=fa[p]) ch[p][c]=np;
if(!p) fa[np]=1;
else{
int q=ch[p][c];
if(l[q]==l[p]+1) fa[np]=q;
else{
int nq=++tot;l[nq]=l[p]+1;
memcpy(ch[nq],ch[q],sizeof(ch[q]));
fa[nq]=fa[q];fa[q]=fa[np]=nq;
for(;ch[p][c]==q;p=fa[p]) ch[p][c]=nq;
}
}
}
void build(){
scanf("%s",s+1);scanf("%s",t+1);n=strlen(t+1);int len=strlen(s+1);
last=tot=1;for(int i=1;i<=len;i++)ins(s[i]-'a');
}
void calc(){len=0;
int p=1;
for(int i=1;i<=n;i++){
int c=t[i]-'a';
if(ch[p][c]) len++,p=ch[p][c];
else{
for(;p&&!ch[p][c];p=fa[p]);
if(p) len=l[p]+1,p=ch[p][c];
else len=0,p=1;
}
ans=max(ans,len);
}
cout<<ans<<endl;
}
}sam;
int main(){
sam.build();
sam.calc();
}
二.恐吓信(usaco2011dec)
和LCS那个差不多,还要简单一点,回原点的时候ans+1
Code(略)
三.[SDOI2016]生成魔咒
几乎是模板题,插入一个字符会生成一个新Suffix和它的所有后缀,贡献为它到后缀链接的长度
Code:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
inline int read(){
int res=0,f=1;char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') f=-f;ch=getchar();}
while(isdigit(ch)) {res=(res<<3)+(res<<1)+(ch^48);ch=getchar();}
return res*f;
}
const int N=2000005;
int n,add;
ll ans=0;
map<int,int>ch[N];
int fa[N],l[N],last,tot;
inline int calc(int x){return l[x]-l[fa[x]];}
void ins(int c){
int flag=0;
int p=last,np=++tot;last=np;l[np]=l[p]+1;
for(;p&&!ch[p][c];p=fa[p]) ch[p][c]=np;
if(!p) fa[np]=1,ans+=calc(np);
else{
int q=ch[p][c];
if(l[q]==l[p]+1) fa[np]=q,ans+=calc(np);
else{
int nq=++tot;l[nq]=l[p]+1;
ch[nq]=ch[q];
fa[nq]=fa[q];ans+=calc(nq)-calc(q);
fa[q]=fa[np]=nq;ans+=calc(np)+calc(q);
for(;ch[p][c]==q;p=fa[p]) ch[p][c]=nq;
}
}
}
void work(){
tot=last=1;n=read();
for(int i=1;i<=n;i++) {add=read();ins(add);cout<<ans<<'\n';}
}
int main(){
work();
return 0;
}
四.[ZJOI2015]诸神眷顾的幻想乡
广义后缀自动机,把trie树上的每个叶子节点(此题只有20个)来倒着插入SAM,然后统计答案就行
Code:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
inline int read(){
int res=0,f=1;char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') f=-f;ch=getchar();}
while(isdigit(ch)) {res=(res<<3)+(res<<1)+(ch^48);ch=getchar();}
return res*f;
}
const int N=2000010;
char s[N];
int vis[N],head[N],nxt[N],cnt=0;
inline void add(int x,int y){vis[++cnt]=y,nxt[cnt]=head[x],head[x]=cnt;}
namespace SAM{
int in[N],c[N];
int n,m;
int l[N],fa[N],ch[N][26],last,tot;
inline int ins(int c,int last){
int p=last,np=++tot;last=np;l[np]=l[p]+1;
for(;p&&!ch[p][c];p=fa[p]) ch[p][c]=np;
if(!p) fa[np]=1;
else{
int q=ch[p][c];
if(l[q]==l[p]+1) fa[np]=q;
else{
int nq=++tot;l[nq]=l[p]+1;
memcpy(ch[nq],ch[q],sizeof(ch[q]));
fa[nq]=fa[q];fa[q]=fa[np]=nq;
for(;ch[p][c]==q;p=fa[p]) ch[p][c]=nq;
}
}
return np;
}
void dfs(int x,int y,int l){
int la=ins(c[x],l);
for(int i=head[x];i;i=nxt[i]) if(vis[i]!=y) dfs(vis[i],x,la);
}
void work(){
n=read();m=read();tot=1;
for(int i=1;i<=n;i++) c[i]=read();
for(int i=1;i<=n-1;i++){
int x=read(),y=read();
add(x,y);add(y,x);
in[x]++;in[y]++;
}
for(int i=1;i<=n;i++) if(in[i]==1) dfs(i,0,1);
long long ans=0;
for(int i=1;i<=tot;i++) ans+=l[i]-l[fa[i]];
printf("%lld\n",ans);
}
}
signed main(){
SAM::work();
return 0;
}
五.Musical Themes
实际上这题原数据n=20000,
O
(
n
2
)
O(n^2)
O(n2)显然不可做
我们发现变调虽然会改变数值大小,但并不会影响相邻两个数之间的差值,所以我们把所有数与相邻的两个数作差,将得到的值插入SAM,然后就是求一个串的最长重复子串(重复不能重叠)的问题了,会处理right的童鞋应该就都会了
不会处理right的话可以看看DZYO的博客
Code(略)
六.[NOI2015]品酒大会
NOI2015Day1真的水 并查集离散化签到题,树剖果题都考了
先把串倒着插入,这样前缀就变成了后缀
发现串如果是r相似的,那么他们一定是r-1,r-2,…1,0相似的
所以可以用类似于前缀和的方式统计答案,然后就好做了
Code:
#include<bits/stdc++.h>
#define ll long long
#define inf 1000000000000000000ll
using namespace std;
inline int read(){
int res=0,f=1;char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') f=-f;ch=getchar();}
while(isdigit(ch)) {res=(res<<3)+(res<<1)+(ch^48);ch=getchar();}
return res*f;
}
const int N=600005;
char s[N];
int vis[N],head[N],nxt[N],cnt=0;
inline void add(int x,int y){vis[++cnt]=y,nxt[cnt]=head[x],head[x]=cnt;}
ll lmax(ll a,ll b){return a>b?a:b;}
ll lmin(ll a,ll b){return a<b?a:b;}
ll ans1[N],ans2[N];
namespace SAM{
int n;
int siz[N];
ll mx[N],mn[N];
int l[N],fa[N],ch[N][26],last,tot;
inline void ins(int c,int w){
int p=last,np=++tot;last=np;l[np]=l[p]+1;
siz[np]=1,mx[np]=mn[np]=w;
for(;p&&!ch[p][c];p=fa[p]) ch[p][c]=np;
if(!p) fa[np]=1;
else{
int q=ch[p][c];
if(l[q]==l[p]+1) fa[np]=q;
else{
int nq=++tot;l[nq]=l[p]+1;
memcpy(ch[nq],ch[q],sizeof(ch[q]));
fa[nq]=fa[q];fa[q]=fa[np]=nq;
for(;ch[p][c]==q;p=fa[p]) ch[p][c]=nq;
}
}
}
inline void build(){
for(int i=2;i<=tot;i++)
add(fa[i],i);
}
inline bool check(int x){return mx[x]!=-inf&&mn[x]!=inf;}
void dfs(int v){
if(!mn[v]&&!mx[v]) mn[v]=inf,mx[v]=-inf;
for(int i=head[v];i;i=nxt[i]){
int y=vis[i];dfs(y);
if(check(v)&&check(y)) ans2[l[v]]=lmax(ans2[l[v]],lmax(mx[v]*mx[y],mn[v]*mn[y]));
ans1[l[v]]+=(ll)siz[v]*siz[y];
mx[v]=lmax(mx[v],mx[y]);
mn[v]=lmin(mn[v],mn[y]);
siz[v]+=siz[y];
}
}
ll a[N];
void work(){
n=read();scanf("%s",s+1);
for(int i=1;i<=n;i++) a[i]=read();
last=tot=1;for(int i=n;i>=1;i--) ins(s[i]-'a',a[i]);
for(int i=0;i<=n;i++) ans2[i]=-inf;
build();dfs(1);
for(int i=n-1;i>=0;i--)
ans2[i]=lmax(ans2[i],ans2[i+1]),ans1[i]+=ans1[i+1];
for(int i=0;i<=n-1;i++) printf("%lld %lld\n",ans1[i],ans1[i]?ans2[i]:0);
}
}
int main(){
SAM::work();
return 0;
}