HDU 2825 Wireless Password(AC自动机)

Wireless Password

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


Problem Description
Liyuan lives in a old apartment. One day, he suddenly found that there was a wireless network in the building. Liyuan did not know the password of the network, but he got some important information from his neighbor. He knew the password consists only of lowercase letters 'a'-'z', and he knew the length of the password. Furthermore, he got a magic word set, and his neighbor told him that the password included at least k words of the magic word set (the k words in the password possibly overlapping).

For instance, say that you know that the password is 3 characters long, and the magic word set includes 'she' and 'he'. Then the possible password is only 'she'.

Liyuan wants to know whether the information is enough to reduce the number of possible passwords. To answer this, please help him write a program that determines the number of possible passwords.
 

 

Input
There will be several data sets. Each data set will begin with a line with three integers n m k. n is the length of the password (1<=n<=25), m is the number of the words in the magic word set(0<=m<=10), and the number k denotes that the password included at least k words of the magic set. This is followed by m lines, each containing a word of the magic set, each word consists of between 1 and 10 lowercase letters 'a'-'z'. End of input will be marked by a line with n=0 m=0 k=0, which should not be processed.
 

 

Output
For each test case, please output the number of possible passwords MOD 20090717.
 

 

Sample Input
10 2 2 hello world 4 1 1 icpc 10 0 0 0 0 0
 

 

Sample Output
2 1 14195065
 
分析:这道题涉及到了多个字符串的匹配,我们可以想到AC自动机,一开始看这道题的状态数很多,构造单词的方式有很多种,所以我们需要进行状态压缩
dp[i][j][k]表示当前的字符串长度为i,在自动机的结点j上,并且出现过的表列里的字符串为k,k包含了里面出现的字符串的信息
这里的num数组用来存特征值k对应的单词数量
代码如下:
#include <stdio.h>
#include <algorithm>
#include <iostream>
#include <string.h>
#include <queue>
using namespace std;
int num[1<<10+10];
int dp[30][110][1<<10];
const int MOD=20090717;
int n,m,s;
struct Trie
{
    int Next[110][26];//26是这里讨论26个小写字母的情况,根据情况修改
    int fail[110],end[110];//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 id)
    {
        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]|=(1<<id);//以该节点结尾的单词的特征值
    }
    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 solve()
    {
        int ans=0;
      for(int i=0;i<=n;i++)
        for(int j=0;j<L;j++)
         for(int k=0;k<(1<<m);k++)
         dp[i][j][k]=0;
       dp[0][0][0]=1;
       for(int i=0;i<n;i++)
        for(int j=0;j<L;j++)
         for(int k=0;k<(1<<m);k++)
       {
           if(dp[i][j][k]>0)
           {
               for(int r=0;r<26;r++)
               {
                 int newi=i+1;
                 int newj=Next[j][r];
                 int newk=(k|end[newj]);
                 dp[newi][newj][newk]+=dp[i][j][k];
                 dp[newi][newj][newk]%=MOD;
              //    cout<<newi<<" "<<newj<<" "<<newk<<" "<<dp[newi][newj][newk]<<endl;
               }
           }
       }
         for(int k=0;k<(1<<m);k++)
         {
             if(num[k]<s)continue;
            for(int j=0;j<L;j++)
           {
             ans+=dp[n][j][k];
             ans%=MOD;
           }
         }
        return ans;
    }
};
char buf[110];
Trie ac;
int main()
{

    for(int i=0;i<(1<<10);i++)
    {
        num[i]=0;
        for(int j=0;j<10;j++)
        if(i&(1<<j))
        num[i]++;
    }
    while(scanf("%d%d%d",&n,&m,&s)!=EOF){
        if(n==0&&m==0&&s==0)break;
        ac.init();
       for(int i=0;i<m;i++)
       {
           scanf("%s",buf);
           ac.insert(buf,i);
       }
           ac.build();
         cout<<ac.solve()<<endl;
    }
    return 0;
}

 

 
 
posted @ 2017-10-07 00:49  hinata_hajime  阅读(170)  评论(0编辑  收藏  举报