• bzoj1195 [HNOI2006]最短母串(状压dp)


    题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1195

    其实我写麻烦了。。。

    设dp[i][j]表示在i这个状态下,我们连接这些字符串且以第j个字符串为结尾所形成的字符串最短为少,g[i][j]储存这个状态下的字符串是什么。bin[i]=2^i;

    那么转移方程就是dp[i|bin[k]][j]=min(dp[i|bin[k]][j],dp[i][j]+(把第k个字符串连到第j个字符串后面所会增加的长度));(j是我们枚举的i状态中已连接的字符串,k是i状态中还未连接的字符串)

    很明显,那些连接任意两个字符串所要增加的长度可以事先处理出来。g数组的处理也差不多,只不过加的是把第k个字符串连到第j个字符串后面所会增加的字符串(除去公共部分)。

    接下来说一些小细节:

    1.首先,如果一个字符串是其他串的子串的话,就直接把它除去,我在程序中判断子串是用kmp的),为了练习一下,其实直接枚举就好了。

    2.其次,我们要怎么判断两个字串相连的公共部分最长是多少呢,其实是O(n)扫一遍,这个程序里面详细写。

    唔,其他细节程序里写。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #include<string>
    #define maxx 100000000
    using namespace std;
    int ans,len[30][30],dp[6009][30],n,next[30],bin[30];
    string a[30],b[30],g[6009][30],len2[30][30];
    bool check(string&s1,string&s2)
    //检查s1是不是s2的子串,我用了kmp,还是建议枚举。
    {
      int l1=s1.size(),l2=s2.size();
      if (l1>l2) return false;
      int now=-1; next[0]=-1;
      for (int i=1;i<l1;i++)
      {
          while ((now!=-1)&&(s1[now+1]!=s1[i])) now=next[now];
          if (s1[now+1]==s1[i]) now++;
          next[i]=now;
      }
      now=-1;
      for (int i=0;i<l2;i++)
      {
          while ((now!=-1)&&(s2[i]!=s1[now+1])) now=next[now];
          if (s2[i]==s1[now+1]) now++;
          if (now==l1-1) return true;
      }
      return false;
    }
    void calc(int x,int y)
    //计算第x和第y的子串,连接他们会增加的长度,和增加的字符串
    {
      string s1=a[x],s2=a[y];
      int tot=0,l1=s1.size(),l2=s2.size();
      int h1=l1-1,h2=0;
      string p1,p2;
      while ((h1>=0)&&(h2<l2))
      {
          p1=s1[h1--]+p1; p2=p2+s2[h2++];
          if (p1==p2) tot=max(tot,h2);
      }
      len[x][y]=l2-tot;
      for (int i=tot;i<l2;i++) len2[x][y]=len2[x][y]+s2[i];
    //这个是如果连接着两个字符串的话,在第X个字符串后面会增加的字符串是多少
    }
    int main()
    {
      scanf("%d",&n);
      char s[100];
      for (int i=1;i<=n;i++) 
      {
        scanf("%s",s);
        string t;
        for (int j=0;j<strlen(s);j++) t=t+s[j];
        b[i]=t; 
      }
      int cnt=0;
      for (int i=1;i<=n;i++)
      {
       bool fg=false;
       for (int j=1;j<=n;j++)
       if ((i!=j)&&(check(b[i],b[j]))) 
       {
            fg=true;
            break;
       }
       if (!fg) a[++cnt]=b[i];
      }
    //删去被别人包含的字符串
      n=cnt;
      if (n==0) cout<<b[1]<<endl;//如果字符串全部相同的话,就不用计算了
      else
      {
       for (int i=1;i<=n;i++)
       for (int j=1;j<=n;j++)
       if (i!=j) calc(i,j);
    //计算两个字符串连接后会增加的部分
       bin[0]=1;
       for (int i=1;i<=n;i++) bin[i]=bin[i-1]*2;
       for (int i=0;i<bin[n];i++)
       for (int j=1;j<=n;j++) dp[i][j]=maxx;
       for (int i=1;i<=n;i++) 
       {
        dp[bin[i-1]][i]=a[i].size();
        g[bin[i-1]][i]=a[i];
       } 
    //dp
       for (int i=0;i<bin[n];i++)
       for (int j=1;j<=n;j++)
       if ((i&bin[j-1])&&(dp[i][j]!=maxx))
       for (int k=1;k<=n;k++)
       if (!(i&bin[k-1]))
       {
          if (dp[i|bin[k-1]][k]==dp[i][j]+len[j][k])
        g[i|bin[k-1]][k]=min(g[i|bin[k-1]][k],g[i][j]+len2[j][k]);
        if (dp[i|bin[k-1]][k]>dp[i][j]+len[j][k])
        {
          dp[i|bin[k-1]][k]=dp[i][j]+len[j][k];
          g[i|bin[k-1]][k]=g[i][j]+len2[j][k];
        }
       }
       ans=maxx; string ans2;
    //统计答案
       for (int i=1;i<=n;i++)
       if (dp[bin[n]-1][i]<ans)
       {
          ans=dp[bin[n]-1][i];
          ans2=g[bin[n]-1][i];
       }
       else if (dp[bin[n]-1][i]==ans)
       ans2=min(ans2,g[bin[n]-1][i]);
       cout<<ans2<<endl;
      }
      return 0;
    }
  • 相关阅读:
    laravel 汇总数据
    Sway
    利用 Windows API Code Pack 修改音乐的 ID3 信息
    Windows Server 2012 R2 设置 NTP 服务
    Visual Studio "14" CTPs
    Win8.1 查看 “Windows 体验指数“
    json2csharp & json 格式化
    山寨版 WP8.1 Cortana 启动 PC
    Newtonsoft.Json WindowPhone7.1
    Cisco交换机基础命令 + Win Server08 R2 多网卡配置链路聚合
  • 原文地址:https://www.cnblogs.com/2014nhc/p/7921891.html
Copyright © 2020-2023  润新知