Total Submission(s): 5502 Accepted Submission(s): 1737
扔了大半年的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
#include
#include
#include
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;iQ;
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;i0)
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<