http://www.elijahqi.win/archives/2796
题意:给定一串字符求其等级 对于等级的定义是
字符串中出现至少两次的等级最高的字符串的等级+1
那么有个显然的n^3 暴力dp+hash的做法设dp[i][j]表示i开始长度为j的这段区间的等级是多少 每次直接比较前缀后缀 即可 据说gzez某大佬有hash 的做法可以ac 复杂度o(能过) 我不会..
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 5010
#define mod 1004535809
#define ll unsigned long long
#define g1 131
#define g2 27
using namespace std;
char s[N];ll p1[N],hs1[N];
int dp[N][N];int p2[N],hs2[N];
inline ll calc1(int l,int r){
return hs1[r]-hs1[l-1]*p1[r-l+1];
}
inline int calc2(int l,int r){
return ((ll)hs2[r]-(ll)hs2[l-1]*p2[r-l+1]%mod+mod)%mod;
}
inline bool judge(int l,int r){
bool flag1=1,flag2=1;
flag1&=calc1(l,r-1)==calc1(l+1,r);
flag2&=calc2(l,r-1)==calc2(l+1,r);
return flag1&&flag2;
}
inline bool check(int l1,int r1,int l2,int r2){
bool flag1=1,flag2=1;
flag1&=(calc1(l1,r1)==calc1(l2,r2));
flag2&=(calc2(l1,r1)==calc2(l2,r2));
return flag1&&flag2;
}
int main(){
// freopen("t1.in","r",stdin);
scanf("%s",s+1);int n=strlen(s+1);p1[0]=1;p2[0]=1;
for (int i=1;i<=n;++i) dp[i][1]=1,p1[i]=p1[i-1]*g1,p2[i]=(ll)p2[i-1]*g2%mod,
hs1[i]=hs1[i-1]*g1+s[i]-'a'+1,hs2[i]=((ll)hs2[i-1]*g2+s[i]-'a'+1)%mod;
for (int j=2;j<=n;++j)
for (int i=1;i+j<=n+1;++i){int mx=-1;
for (int k=1;k<=j-1;++k){
if (check(i,i+k-1,i+j-k,i+j-1)) mx=max(mx,max(dp[i][k],dp[i+j-k][k]));
}if (mx==-1) dp[i][j]=max(dp[i][j-1],dp[i+1][j-1]);
else dp[i][j]=max(mx+1, max(dp[i][j-1],dp[i+1][j-1]));
}
printf("%d\n",dp[1][n]);
return 0;
}
SAM+可持久化线段树 做法:
假如有一个k级的东西 那么显然他可以由两个k-1的前缀和后缀组成 同理两个k-1的前缀和后缀也可以由k-2的后缀等 组成
所以我们可以先针对整个串建立sam 然后在par树上倒着从下往上推 每次把合并两个节点的right集合 考虑一个字符串A 如果开头和结尾都是比他低一个等级的字符串B 那么显然B 不会和A在同一个节点 因为right集合不相同 那么结论显然 所以只需要判断我这个串包含的最大的等级的那个 串有没有出现两次如果有就 给我自己的答案+1反之 我的答案就等于那个串的答案 这部分建可持久化线段树即可 因为是出现两次 所以每次去找一找有没有这个点即可
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 200010
using namespace std;
inline char gc(){
static char now[1<<16],*S,*T;
if (T==S){T=(S=now)+fread(now,1,1<<16,stdin);if (T==S) return EOF;}
return *S++;
}
int len[N<<1],fa[N<<1],cnt=1,c[N],last=1,root=1,id[N<<1],ch[N<<1][26],rk[N<<1],rt[N<<1],dp[N<<1],dp1[N<<1];
inline void insert1(int x,int idx){
int np=++cnt,p=last;len[np]=len[p]+1;fa[np]=p;id[np]=idx;
for (;p&&!ch[p][x];p=fa[p]) ch[p][x]=np;
if (!p) fa[np]=root;else {
int q=ch[p][x];if (len[p]+1==len[q]) fa[np]=q;else{
int nq=++cnt;memcpy(ch[nq],ch[q],sizeof(ch[q]));fa[nq]=fa[q];fa[q]=fa[np]=nq;
id[nq]=idx;len[nq]=len[p]+1;for (;p&&ch[p][x]==q;p=fa[p]) ch[p][x]=nq;
}
}last=np;
}
struct node{int left,right;}tree[N*40];
inline void insert2(int &x,int l,int r,int p){
if (!x) x=++cnt;if (l==r) return;int mid=l+r>>1;
if (p<=mid) insert2(tree[x].left,l,mid,p);else insert2(tree[x].right,mid+1,r,p);
}
inline int merge(int p1,int p2){
if (!p1||!p2) return p1+p2;int owo=++cnt;
tree[owo].left=merge(tree[p1].left,tree[p2].left);
tree[owo].right=merge(tree[p1].right,tree[p2].right);
return owo;
}
inline bool query(int x,int l,int r,int l1,int r1){
if (!x) return 0;if (l1<=l&&r1>=r) return 1;int mid=l+r>>1;
if (l1<=mid&&query(tree[x].left,l,mid,l1,r1)) return 1;
if (r1>mid&&query(tree[x].right,mid+1,r,l1,r1)) return 1;return 0;
}
int main(){
freopen("t1.in","r",stdin);
char ch=gc();int n=0;while(ch<'a'||ch>'z') ch=gc();
while(ch>='a'&&ch<='z') ++n,insert1(ch-'a',n),ch=gc();
for (int i=1;i<=cnt;++i) ++c[len[i]];int nn=cnt;
for (int i=1;i<=n;++i) c[i]+=c[i-1];cnt=0;
for (int i=nn;i;--i) rk[c[len[i]]--]=i;
for (int i=nn;i;--i){int x=rk[i];
insert2(rt[x],1,n,id[x]);rt[fa[x]]=merge(rt[fa[x]],rt[x]);
}int ans=1;
for (int i=2;i<=nn;++i){int x=rk[i];
if (fa[x]==root) {dp[x]=1,dp1[x]=x;continue;}
if (query(rt[dp1[fa[x]]],1,n,id[x]-len[x]+len[dp1[fa[x]]],id[x]-1)) dp[x]=dp[fa[x]]+1,dp1[x]=x,ans=max(ans,dp[x]);
else dp[x]=dp[fa[x]],dp1[x]=dp1[fa[x]];
}printf("%d\n",ans);
return 0;
}