首页 > 代码库 > SPOJ 7758. Growing Strings (ac自动机+dp)

SPOJ 7758. Growing Strings (ac自动机+dp)

题目大意:

给出了N个串。问最多有多少个串组成的序列,是可以由上一个串通过左右两边加字符构成的。


思路分析:

在trie上的dp

在建立自动机的时候,得到fail的同时,用dp记录这个串作为最后一个串所可以得到的最多的满足要求的串的数量。

那么 dp[i] = max(dp[i在trie上的的父亲节点],dp[i的fail节点] )+ 以i节点结尾的单词的数量,注意不是以i字符结尾。


#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#define maxn 1000005
using namespace std;
const char tab = 'a';
const int max_next = 26;

int next[maxn][max_next],fail[maxn],num[maxn],dp[maxn],siz;
int newnode()
{
    for(int i=0;i<max_next;i++)
        next[siz][i]=0;
    fail[siz]=num[siz]=dp[siz]=0;
    return siz++;
}
void Insert(char *s,int len)
{
    int p=0;
    for(int i=0;i<len;i++)
    {
        int &x=next[p][s[i]-tab];
        p=x?x:x=newnode();
    }
    num[p]++;
}

int ans=0;
void acbuild()
{
    queue<int>Q;
    Q.push(0);
    while(!Q.empty())
    {
        int temp=Q.front();
        Q.pop();
        for(int i=0;i<max_next;i++)
        {
            int v=next[temp][i];
            if(v==0)next[temp][i]=next[fail[temp]][i];
            else Q.push(v);
            if(temp!=0)fail[v]=next[fail[temp]][i];

            dp[v]=max(dp[temp],dp[fail[v]])+num[v];
            ans=max(ans,dp[v]);
        }
    }
}
int query(char *s,int len)
{
    int cnt=0;
    for(int i=0,p=0;i<len;i++)
    {
        p=next[p][s[i]-tab];
        for(int f=p;f;f=fail[f]){
            if(num[f]>0)cnt++;
        }
    }
    return cnt;
}
char word[maxn];

int main()
{
    int N;
    while(scanf("%d",&N)!=EOF && N)
    {
        ans=0;
        siz=0;
        newnode();

        for(int i=1;i<=N;i++)
        {
            scanf("%s",word);
            Insert(word,strlen(word));
        }

        acbuild();
        printf("%d\n",ans);
    }
    return 0;
}


SPOJ 7758. Growing Strings (ac自动机+dp)