计蒜客 新年礼物 (trie+exkmp+dp)

 刚开始拿上这道题,没有一点思路,看了提示,说kmp处理前后缀,才恍然大悟,加上dp处理结果,妙哉。

#include<bits/stdc++.h>

using namespace std;

const int MAX_N = 2e6+10;  // Trie 树上的最大结点数

const int MAX_C = 26;  // 每个结点的子结点个数上限

int dp[MAX_N];

struct Trie {
    int *ch[MAX_N];  // ch 保存了每个结点的 26 个可能的子结点编号,26 对应着 26 种小写字母,也就是说,插入的字符串全部由小写字母组成。初始全部为 -1
    int tot;  // 总结点个数(不含根结点),初始为 0
    int cnt[MAX_N];  // 以当前结点为终端结点的 Trie 树中的字符串个数

    void init() {  // 初始化 Trie 树,根结点编号始终为 0
        tot = 0;
        memset(cnt, 0, sizeof(cnt));
        memset(ch, 0, sizeof(ch));  // 将 ch 中的元素初始化为 NULL
    }

    void insert(char *str,int *Next,int idx) {
        int p = 0;  // 从根结点(0)出发
        for (int i = 0; str[i]; ++i) {
            if (ch[p] == NULL) {
                ch[p] = new int[MAX_C];  // 只有 p 当包含子结点的时候才去开辟 ch[p] 的空间
                memset(ch[p], -1, sizeof(int) * MAX_C);  // 初始化为 -1
            }
            if (ch[p][str[i] - 'A'] == -1) {  // 该子结点不存在
                ch[p][str[i] - 'A'] = ++tot;  // 新增结点
            }
            p = ch[p][str[i] - 'A'];  // 在 Trie 树上继续插入字符串 str+
            if(cnt[p]){
                if(Next[strlen(str)-i-1]==i+1){
                    dp[idx]=max(dp[idx],dp[cnt[p]]+1);
                }
            }
        }
        cnt[p]=idx;
    }

}trie;

int Next[MAX_N];

void getnext(char* t)
{
    int i,len=strlen(t);
    Next[0]=len;
    for(i=0;i<len-1&&t[i]==t[i+1];i++);
    Next[1]=i;
    int a=1;
    for(int k=2;k<len;k++){
        int p=a+Next[a]-1,l=Next[k-a];
        if(k+l-1>=p){       //l>=p-k+1
            int j=max(p-k+1,0);
            while(k+j<len&&t[k+j]==t[j]) j++;
            Next[k]=j;a=k;
        }else Next[k]=l;
    }
}
char s[MAX_N];
int main() {
	int n;
    
    scanf("%d",&n);
    trie.init();
    for(int i=1;i<=n;i++){
        scanf("%s",s);
        getnext(s);
        trie.insert(s,Next,i);
    }
    int ans=0;
    for(int i=1;i<=n;i++){
        ans=max(ans,dp[i]);
    }
    printf("%d\n",ans+1);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_40679299/article/details/82023353