1. 程式人生 > >HDU 2296 Ring (AC自動機+DP)

HDU 2296 Ring (AC自動機+DP)

ech when more uil 返回 size 並且 printf 枚舉

Ring

Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 3966 Accepted Submission(s): 1316


Problem Description For the hope of a forever love, Steven is planning to send a ring to Jane with a romantic string engraved on. The string‘s length should not exceed N. The careful Steven knows Jane so deeply that he knows her favorite words, such as "love", "forever". Also, he knows the value of each word. The higher value a word has the more joy Jane will get when see it.
The weight of a word is defined as its appeared times in the romantic string multiply by its value, while the weight of the romantic string is defined as the sum of all words‘ weight. You should output the string making its weight maximal.

Input The input consists of several test cases. The first line of input consists of an integer T, indicating the number of test cases. Each test case starts with a line consisting of two integers: N, M, indicating the string‘s length and the number of Jane‘s favorite words. Each of the following M lines consists of a favorite word Si. The last line of each test case consists of M integers, while the i-th number indicates the value of Si.
Technical Specification

1. T ≤ 15
2. 0 < N ≤ 50, 0 < M ≤ 100.
3. The length of each word is less than 11 and bigger than 0.
4. 1 ≤ Hi ≤ 100.
5. All the words in the input are different.
6. All the words just consist of ‘a‘ - ‘z‘.

Output For each test case, output the string to engrave on a single line.
If there‘s more than one possible answer, first output the shortest one. If there are still multiple solutions, output the smallest in lexicographically order.

The answer may be an empty string.

Sample Input 2 7 2 love ever 5 5 5 1 ab 5

Sample Output lovever abab 分析: 枚舉構造的所有的可能性,用DP記錄最大值 這裏 AC自動機中end數組記錄在該節點的價值. 並且如果我們走到這個節點的話,就會獲得這個節點的價值, 那麽就會想到我們也能得到是它的前綴的節點的價值,所以end節點記錄該節點和前綴節點的價值之和, 用fail數組的性質我們就能實現這個. 字典序要求最小,我們在DP的時候和最後找結果的時候,都進行一下字符串大小的比較就可以了 代碼如下:
#include <stdio.h>
#include <algorithm>
#include <iostream>
#include <string.h>
#include <queue>
#include <string>
using namespace std;
#define INF 0x3f3f3f3f
struct node
{
  int x;
  string a;
}dp[55][1500];
int maxx;
string maxstr;
string b;
char str[110][15];
int val[110];
struct Trie
{
    int Next[1500][26];//26是這裏討論26個小寫字母的情況,根據情況修改
    int fail[1500],end[1500];//end數組表示以該節點結尾的字符串的數量
    int root,L;//L用來標記節點序號,以廣度優先展開的字典樹的序號
    int newnode()  //建立新節點
    {
        for(int i = 0;i < 26;i++)
            Next[L][i] = -1;     //將該節點的後繼節點域初始化
        end[L++] = 0;
        return L-1;    //返回當前節點編號
    }
    void init() //初始化操作
    {
        L = 0;
        root = newnode();
    }
    void insert(char buf[],int val)
    {
        int len = strlen(buf);
        int now = root;
        for(int i = 0;i < len;i++)
        {
            if(Next[now][buf[i]-a] == -1)  //如果未建立當前的後繼節點,建立新的節點
                Next[now][buf[i]-a] = newnode();
            now = Next[now][buf[i]-a];
        }
        end[now]+=val;//以該節點結尾的字符串數量增加1
    }
    void build()
    {
        queue<int>Q; //用廣度優先的方式,將樹層層展開
        fail[root] = root;
        for(int i = 0;i < 26;i++)
            if(Next[root][i] == -1)
                Next[root][i] = root;
            else
            {
                fail[Next[root][i]] = root;
                Q.push(Next[root][i]);
            }
        while( !Q.empty() )
        {
            int now = Q.front();
            Q.pop();
            end[now]+=end[fail[now]];
            for(int i = 0;i < 26;i++)
                if(Next[now][i] == -1)
                    Next[now][i] = Next[fail[now]][i];//該段的最後一個節點匹配後,跳到擁有最大公共後綴的fail節點繼續匹配
                else
                {
                    fail[Next[now][i]]=Next[fail[now]][i];//當前節點的fail節點等於它前驅節點的fail節點的後繼節點
                    Q.push(Next[now][i]);
                }
        }
    }
    int query(char buf[])
    {
        int len = strlen(buf);
        int now = root;
        int res = 0;
        for(int i = 0;i < len;i++)
        {
            now = Next[now][buf[i]-a];
            int temp = now;
            while( temp != root )
            {
                res += end[temp];//加上以當前節點結尾的字符串數
                end[temp] = 0;//該題是防止計算重復的字符串
                temp = fail[temp];//每次找最大公共後綴對應的fail節點
            }
        }
        return res;
    }
    void solve(int len)
    {
        for(int i=0;i<=len;i++)
            for(int j=0;j<L;j++){
            dp[i][j].x=INF;
            dp[i][j].a.clear();
            }
        dp[0][0].x=0;
        for(int i=0;i<len;i++)
            for(int j=0;j<L;j++)
        {
            if(dp[i][j].x<INF)
            {
               for(int k=0;k<26;k++)
               {
                  int news=Next[j][k];
                  if(dp[i+1][news].x==INF||dp[i+1][news].x<dp[i][j].x+end[news])
                  {
                     dp[i+1][news].x=dp[i][j].x+end[news];
                     dp[i+1][news].a=dp[i][j].a+(char)(a+k);
                  }
                 else if(dp[i+1][news].x==dp[i][j].x+end[news]&&((dp[i+1][news].a.size()>dp[i][j].a.size()+1)||((dp[i+1][news].a.size()==dp[i][j].a.size()+1)&&dp[i+1][news].a>(dp[i][j].a+(char)(a+k)))))
                 {
                       dp[i+1][news].x=dp[i][j].x+end[news];
                     dp[i+1][news].a=dp[i][j].a+(char)(a+k);
                 }
               }
            }
        }
       maxx=0;
       maxstr="";
       for(int i=1;i<=len;i++)
            for(int j=0;j<L;j++)
       {
           if(dp[i][j].x<INF){
             if((maxx<dp[i][j].x)||(maxx==dp[i][j].x&&dp[i][j].a.size()<maxstr.size())||(maxx==dp[i][j].x&&dp[i][j].a.size()==maxstr.size()&&dp[i][j].a<maxstr)){
              maxx=dp[i][j].x;
              maxstr=dp[i][j].a;
             }
           }
       }
    }
    void debug()
    {
        for(int i = 0;i < L;i++)
        {
            printf("id = %3d,fail = %3d,end = %3d,chi = [",i,fail[i],end[i]);
            for(int j = 0;j < 26;j++)
                printf("%2d",Next[i][j]);
            printf("\n");
        }
    }
};
char buf[1500];
Trie ac;
int main()
{
    int  t,n,ans,m;
    scanf("%d",&t);
    while(t--)
    {
        ac.init();
       scanf("%d%d",&n,&m);
         for(int i=0;i<m;i++)
           scanf("%s",str[i]);
        for(int i=0;i<m;i++){
          scanf("%d",&val[i]);
          ac.insert(str[i],val[i]);
        }
       ac.build();
       ac.solve(n);
      cout<<maxstr<<endl;
    }
    return 0;
}

HDU 2296 Ring (AC自動機+DP)