bzoj2580 [Usaco2012 Jan]Video Game AC自动机+dp

挺明显的dp,设f[i][j]表示走到第i个位置,trie上的第j个节点的最大答案,把每个结尾+1然后求出前缀和,直接更新就可以了

#include
#include
#include
#include
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define inf 0xc3c3c3c3
using namespace std;
const int N=1e5+5;
int ch[N][5];
char s[N];
int tag[N],cnt=1;
int fail[N],q[N];
int f[1005][500];
int num[N];
int n,K;
inline void add(int x,int k)
{
    memset(ch[cnt],0,sizeof(ch[cnt]));
    num[cnt]=0;
    ch[x][k]=cnt++;
}
inline void ins()
{
    int n=strlen(s);
    int x=0;
    fo(i,0,n-1)
    {
        int c=s[i]-'A';
        if (!ch[x][c])add(x,c);
         x=ch[x][c];
    }
    num[x]++;
}
inline void acmach()
{
    int w=1;
    int x;
    q[0]=0; 
    fo(i,0,w-1)
    { 
        x=q[i];
        num[x]+=num[fail[x]];
        fo(j,0,2)
        if (ch[x][j])
        {
            q[w++]=ch[x][j];
            if (!x)fail[ch[x][j]]=0;
            else fail[ch[x][j]]=ch[fail[x]][j];
        }
        else ch[x][j]=ch[fail[x]][j];
    }
}
inline void dp()
{
    memset(f,0xc3,sizeof(f));
    f[0][0]=0;
    fo(i,0,K-1)
    fo(j,0,cnt-1)
    if (f[i][j]!=inf)
    fo(k,0,2)
    {
        int x=ch[j][k];
        f[i+1][x]=max(f[i+1][x],f[i][j]+num[x]);
    }//f[i][ch[j][k]]=max(f[i][ch[j][k]],f[i-1][j]+num[ch[j][k]]);
}
int main()
{
    scanf("%d%d",&n,&K);
    num[0]=0;
    memset(ch[0],0,sizeof(ch[0]));
    cnt=1;
    fo(i,1,n)
    {
        scanf("%s",s);
        ins();
    }
    acmach();
    dp();
    int ans=0;
    fo(i,0,cnt-1)ans=max(ans,f[K][i]);
    printf("%d\n",ans);
}

你可能感兴趣的:(bzoj,AC自动机,DP)