• P7537 [COCI2016-2017#4] Rima


    由于题目涉及到后缀,不难想到用 trie 树处理。

    将每个字符串翻转插入 trie,后缀就变成了前缀,方便处理。

    条件 LCS ( A , B ) ≥ max ⁡ ( ∣ A ∣ , ∣ B ∣ ) − 1 \text{LCS}(A,B) \ge \max(|A|,|B|)-1 LCS(A,B)max(A,B)1,说明 ∣ ∣ A ∣ − ∣ B ∣ ∣ ≤ 1 \left||A|-|B|\right|\le1 AB1

    所以两个字符串押韵当且仅当在 trie 树上二者是父子或兄弟关系。

    考虑树型 DP,设 f i f_i fi 表示 i i i 所代表的字符串在序列最右边的最长序列长度, v a l i val_i vali 表示节点 i i i 是否(1/0)有单词, s z i = ∑ x ∈ s o n i v a l x sz_i=\sum\limits_{x\in son_i}val_x szi=xsonivalx

    显然有转移 f i = max ⁡ x ∈ s o n x ( f x ) + s z i − 1 + v a l i f_i=\max\limits_{x\in son_x}(f_x)+sz_i-1+val_i fi=xsonxmax(fx)+szi1+vali

    如果 v a l i = 0 val_i=0 vali=0,说明都没有字符串, f i = 0 f_i=0 fi=0

    更新答案时,记录儿子 f s o n i f_{son_i} fsoni 的最大和次大,再加上剩余儿子和自己的 v a l val val。(感性理解,一条链只有两个位置是“自由”的,由此设出 DP 状态)

    本题卡空间,所以建 trie 树时要动态开点。

    具体实现参见代码。

    #include
    using namespace std;
    const int N=3e6+10;
    int cnt=1,n,f[N],ans;
    char a[N];
    struct node
    {
        vector<pair<int,int> > son;
        int fa,val;
    }tr[N];
    void insert(int rt,char a[],int len)
    {
        for(int i=0;i<len;i++){
            int x=a[i]-97;
            for(auto j:tr[rt].son){
                if(j.first==x){
                    rt=j.second;
                    goto a;
                }
            }
            tr[rt].son.push_back(make_pair(x,++cnt));
            rt=tr[rt].son.back().second;
            a:;
        }
        tr[rt].val++;
    }
    void dfs(int rt)
    {
        int aa=0,bb=0,x=0,y=0,sum=0;
        for(auto i:tr[rt].son){
            dfs(i.second);
            sum+=tr[i.second].val;
            f[rt]=max(f[rt],f[i.second]);
            if(f[i.second]>aa){
                bb=aa;
                aa=f[i.second];
                y=x;
                x=tr[i.second].val;
            }
            else if(f[i.second]>bb) bb=f[i.second],y=tr[i.second].val;
        }
        f[rt]+=sum-x;
        if(!tr[rt].son.size()) ans=max(ans,tr[rt].val);
        else ans=max(ans,aa+bb-x-y+sum+tr[rt].val);
        if(!tr[rt].val) f[rt]=0;
        else f[rt]++;
    }
    int main()
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++){
            scanf("%s",a);
            int len=strlen(a);
            reverse(a,a+len);
            insert(1,a,len);
        }
        dfs(1);
        cout<<ans;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
  • 相关阅读:
    VUE3基础知识梳理
    5G/4G/北斗遥测终端机全国各省水利平台无缝对接
    springboot 集成redis
    低代码软件:开发老手的新选择?
    LVGL---使用物理按键代替触摸(groups)
    学习网络安全有哪些误区?学习之前要做哪些准备?如何系统的学习黑客技术/网络安全?
    系统架构设计师【补充知识】: 应用数学 (核心总结)
    StackOverflowError和OutOfMemoryError区别
    js中如何判断浏览器是否被缩放
    棒球省队建设实施办法·棒球1号位
  • 原文地址:https://blog.csdn.net/dygxczn/article/details/133840187