题意:
给你N个字符串, N(1 <= N <= 2w), 所有串的长度加一起不超过30w。每个串有个值。这个值[-1000, 1000].
问不打乱字符串顺序,从中取若干个字符串,使得前一个串是后一个串的子串,求满足前面调条件的字符串值得和最大,求这个值。
思路:
其实就是一个很明显的dp。
dp[i]代表以第i个字符串结尾的最大权值。
但是就是子串这个问题怎么处理。
由于这题数据比较水可以用后缀数组处理这个问题。
将所有字符串拼接,做sa。
每次在height数组里往上和往下寻找公共前缀等于这个大于等于这个串长的那些后缀所对应的是哪个字符串。
然后进行状态转移。
这题的正解应该是AC自动机+dp+线段树优化。
过段时间补全。
后缀数组+dp代码:
#include"stdio.h"
#include"algorithm"
#include"string.h"
#include"iostream"
#include"queue"
#include"map"
#include"vector"
#include"string"
#define N 340005
#define M 20005
using namespace std;
int v[N],sa[N],ra[N],height[N],id[N];
int wa[N],wb[N],wv[N],wws[N];
char fuck[N];
int value[M],mark[M],dp[M],len[M];
int cmp(int *r,int a,int b,int l)
{
return r[a]==r[b]&&r[a+l]==r[b+l];
}
void da(int n,int m)
{
int i,j,p,*x=wa,*y=wb;
for(i=0; i<m; i++) wws[i]=0;
for(i=0; i<n; i++) wws[x[i]=v[i]]++;
for(i=1; i<m; i++) wws[i]+=wws[i-1];
for(i=n-1; i>=0; i--) sa[--wws[x[i]]]=i;
for(j=1,p=1; p<n; j*=2,m=p)
{
for(i=n-j,p=0; i<n; i++) y[p++]=i;
for(i=0; i<n; i++) if(sa[i]>=j) y[p++]=sa[i]-j;
for(i=0; i<n; i++) wv[i]=x[y[i]];
for(i=0; i<m; i++) wws[i]=0;
for(i=0; i<n; i++) wws[wv[i]]++;
for(i=1; i<m; i++) wws[i]+=wws[i-1];
for(i=n-1; i>=0; i--) sa[--wws[wv[i]]]=y[i];
for(swap(x,y),p=1,i=1,x[sa[0]]=0; i<n; i++) x[sa[i]]=cmp(y,sa[i],sa[i-1],j)?p-1:p++;
}
return ;
}
void gethei(int n)
{
int i,j,k=0;
for(i=1; i<=n; i++) ra[sa[i]]=i;
for(i=0; i<n; i++)
{
if(k) k--;
j=sa[ra[i]-1];
while(v[i+k]==v[j+k]) k++;
height[ra[i]]=k;
}
return ;
}
int main()
{
int t,cas=1;
cin>>t;
while(t--)
{
int n,cnt=0,s=0;
scanf("%d",&n);
for(int i=0;i<n;i++)
{
scanf("%s%d",fuck,&value[i]);
dp[i]=value[i];
len[i]=strlen(fuck); //每个串长度
mark[i]=s; //对于每个串取rank[mark[i]] 就是从哪个开始的后缀
s+=len[i]+1;
for(int j=0;fuck[j];j++)
{
id[cnt]=i; //每个后缀对应的是哪个串的
v[cnt++]=fuck[j]-'a'+2;
}
id[cnt]=i;
v[cnt++]=i+55;
}
v[cnt]=0;
da(cnt+1,30000);
gethei(cnt);
for(int i=0;i<n;i++)
{
int Min=N;
for(int j=ra[mark[i]];j>0;j--) //向上找
{
Min=min(Min,height[j]);
if(Min<len[i]) break;
int tep=id[sa[j-1]];
if(tep>i) dp[tep]=max(dp[tep],dp[i]+value[tep]);
}
Min=N;
for(int j=ra[mark[i]]+1;j<=cnt;j++) //向下找
{
Min=min(Min,height[j]);
if(Min<len[i]) break;
int tep=id[sa[j]];
if(tep>i) dp[tep]=max(dp[tep],dp[i]+value[tep]);
}
}
int ans=0;
for(int i=0;i<n;i++) ans=max(ans,dp[i]);
printf("Case #%d: %d\n",cas++,ans);
}
return 0;
}