zoukankan      html  css  js  c++  java
  • 【状态压缩dp】1195: [HNOI2006]最短母串

    一个清晰的思路就是状压dp;不过也有AC自动机+BFS的做法

    Description

    给定n个字符串(S1,S2,„,Sn),要求找到一个最短的字符串T,使得这n个字符串(S1,S2,„,Sn)都是T的子串。

    Input

    第一行是一个正整数n(n<=12),表示给定的字符串的个数。
    以下的n行,每行有一个全由大写字母组成的字符串。每个字符串的长度不超过50.

    Output

    只有一行,为找到的最短的字符串T。在保证最短的前提下,
    如果有多个字符串都满足要求,那么必须输出按字典序排列的第一个。

    Sample Input

    2
    ABCD
    BCDABC

    Sample Output

    ABCDABC

    题目分析

    状压dp

    看到数据范围,自然想到状压dp。$f[t][i]$表示“已经选了$t$这个状态,第$i$个是最后一个选的”状态下最短长度。那么转移时候就是常规的状压dp转移。

    至于处理两个字符串最长公共前后缀长度,我是用hash去做的。当然在AC自动机上根据fail边跳也不失为一种好方法。

     1 #include<bits/stdc++.h>
     2 typedef unsigned int uint;
     3 const int maxn = 103;
     4 const int base = 233;
     5 
     6 std::string str[5003][maxn],s[maxn],sv[maxn],tmp;
     7 int n,all,cnt,mn;
     8 uint power[maxn];
     9 int lens[maxn],num[maxn][maxn],f[5003][maxn];
    10 
    11 int main()
    12 {
    13     memset(f, 0x3f3f3f3f, sizeof f);
    14     scanf("%d",&n);
    15     all = (1<<n)-1, power[0] = 1;
    16     for (int i=1; i<=n; i++)
    17         std::cin >> s[i], lens[i] = s[i].length();
    18     for (int i=1; i<=53; i++) power[i] = power[i-1]*base;  //之前把这个循环放在1..n的循环里了……  以后预处理还是要小心数据范围。
    19     for (int i=1; i<=n; i++)
    20         for (int j=1; j<=n; j++)
    21             if (i^j){
    22                 int l = std::min(lens[i], lens[j]);
    23                 uint val1 = 0, val2 = 0;
    24                 for (int t=0; t<l; t++)
    25                 {
    26                     val1 = val1+power[t]*(s[i][lens[i]-t-1]-'A'+1);
    27                     val2 = val2*base+s[j][t]-'A'+1;
    28                     if (val1==val2) num[i][j] = t+1;
    29                 }
    30             }
    31     mn = f[0][0], f[0][0] = 0;
    32     for (int j=1, tst=1; j<=n; j++, tst=1<<(j-1))
    33         f[tst][j] = lens[j], str[tst][j] = s[j];
    34     for (int p=1; p<all; p++)
    35         for (int i=1, sst=1; i<=n; i++, sst=1<<(i-1))
    36             if (p&sst)
    37                 for (int j=1, tst=1; j<=n; j++, tst=1<<(j-1))
    38                     if (!(p&tst)){
    39                         tmp = str[p][i]+s[j].substr(num[i][j], lens[j]-num[i][j]);
    40                         if (f[p+tst][j] > f[p][i]+lens[j]-num[i][j]){
    41                             f[p+tst][j] = f[p][i]+lens[j]-num[i][j];
    42                             str[p+tst][j] = tmp;
    43                         }else if (f[p+tst][j]==f[p][i]+lens[j]-num[i][j]&&tmp < str[p+tst][j])
    44                             str[p+tst][j] = tmp;  //j和tst一开始没有分清
    45                     }
    46     for (int i=1; i<=n; i++)
    47         if (f[all][i] < mn){
    48             mn = f[all][i], cnt = 1;
    49             sv[cnt] = str[all][i];
    50         }else if (f[all][i]==mn) sv[++cnt] = str[all][i];
    51     std::sort(sv+1, sv+cnt+1);
    52     std::cout << sv[1];
    53     return 0;
    54 }

    AC自动机+BFS

    老早就听说过这个思路,不过写完状压dp去看题解时候才好好想了想。

    这个做法相对来说要抽象一些。不过也算是AC自动机的一种套路应用吧。

    这里按顺序枚举保证了字典序最小;BFS保证了长度最小。

    Bzoj1195 [HNOI2006]最短母串 [AC自动机]

    END

  • 相关阅读:
    无缝世界场景加载的解决方案研究
    3D物体绘制不见
    dx sdk中关于常用dx api的performace性能参数
    OpenGL/DirectX渲染技巧集
    每天送你一個simle
    [原创] 一种页面数据错误输入提示方法
    [原创] ASP.NET 中如何弹出提示窗口然后导向另外一个页面
    [原创] 部署含有ReportView的控件的ASPX页面时出现错误
    公布一个简单的日志记录方法
    [原创] 如何在没有ASP.NET AjaxEnabled Web Site 向导的情况下加入Ajax支持
  • 原文地址:https://www.cnblogs.com/antiquality/p/9671008.html
Copyright © 2011-2022 走看看