Total Submission(s): 5502 Accepted Submission(s): 1737
10 2 2 hello world 4 1 1 icpc 10 0 0 0 0 0
2 1 14195065
扔了大半年的Ac自动机全还回去了,特别不理解先做第一题的人的心理,第一题还得状态压缩,这题就不用压啊
说题意:邻居wifi密码由n个字符组成,由m个子字符串的至少k个组成,问有多少种排列组合的方法。首先复习了一下,AC自动机,next[L][26] fail[L] end[L]分别表示下一个节点的指针,失败指针类似于kmp的next指针,计数的数组(这道题里这个数组不是直接++的,下文会说)
一共有init() insert() build() solve()这些函数,除了最后一个,都是Ac自动机上的模板,大体结构不会变==最后一个函数之前是用来求个数的,其实这次也是,但是需要用dp==
dp[i][j][k]表示长度为i匹配到状态j 各个word出现的情况为k的方法数,解释一下什么意思~i,没得说 1~len;j是状态0~L;k要好好说一下,这个题还是用状压了的,它需要加一个数组num[],二进制中的每一位表示某个单词是否出现过,最最开始的时候初始化使得num[i]储存的值是i中二进制下1的个数,即所包含的单词个数,这个i也就是dp的最后一维。再说说原本用来计数的数组,原本是可以直接++计数的,然而由于状态是相互转化的,所以所有涉及到的地方都是按位或~原本的统计函数是模式串的个数的,从头到尾跑一边,每次+end[]就好,然而这个题是三层状态转移其中第二层也是类似于刚刚说的
/************** hdu2825 2016.3.15 **************/ #include <iostream> #include<cstdio> #include<cstring> #include<queue> using namespace std; #define mod 20090717 int num[1<<11]; int n,m,k; struct Trie { int next[110][26],fail[110],end[110],dp[30][110][1<<10]; int root,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]; else { fail[next[now][i]]=next[fail[now]][i]; Q.push(next[now][i]); } } } int solve() { memset(dp,0,sizeof(dp)); 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 x=0;x<26;x++) { int newi=i+1; int newj=next[j][x];//!! int newk=k|end[newj]; dp[newi][newj][newk]+=dp[i][j][k]; dp[newi][newj][newk]%=mod; } } } } int ans=0; for(int i=0;i<(1<<m);i++) { if(num[i]<k) continue; for(int j=0;j<L;j++) { ans+=dp[n][j][i]; ans%=mod; } } return ans; } }ac; char buf[200]; int main() { //freopen("cin.txt","r",stdin); num[0]=0; 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,&k)) { if(n==0&&m==0&&k==0) break; ac.init(); for(int i=0;i<m;i++) { scanf("%s",buf); ac.insert(buf,i); } ac.build(); printf("%d\n",ac.solve()); } return 0; }