Freeland的字母表中刚好包含了N个字母。Freeland语言(也被称为Freish)中的每一个句子中刚好包含了M个不间断的字母。所以在Freish语言中总共包括了N^M 种不同的句子。
不过最近在Grass Jr.先生当选Freeland总统之后,一些令他不愉快的字句被命令不能刊印,而任何句子之中如果包含有其中的一个都是严禁的。句子S中含有一个单词W, 如果W是一个S的一个子链,例如 存在当k >= 1,k+len(W)-1 <= M的时候 ,有S[k] = W[1], S[k+1] = W[2], … , S[k+len(W)-1] = W[len(W)], len(W)表示W的长度。每个用了一个严禁的词语的人都要被关进监牢10年。
找出一共有多少个能被Freeland的人们使用的不相同的句子,让他们避免因使用严禁的字句而被关进监牢中。
第一行包括了三个integer型数字:
N——是Freish字母表中字母的数目。
M——Freish所有句子的总长度。
P——被禁止使用的字句。(1 <= N <= 50, 1 <= M <= 50, 0 <= P <= 10).
第二行包括了刚好N个不同的字母——Freish语言字母表中的字母(每一个的ASCII码都大于32)。
接下来的P行包括了所有被禁止使用的字句,每一个都不长于Q个字母(Q为M和10之间的较小者),而被禁止的字句中只含有Freish字母表中的字母。
只输出一个integer型的数——Freeland的人们能够安全使用的不相同的句子的总数。
2 3 1
ab
bb
5
较麻烦的题,但是不要被代码长度吓到了
先讲一讲思路,给出了可用字母和不可用单词,
那么首先想到将不可用单词建立AC自动机。
那么什么情况下会用到禁止使用的单词呢?
我们将Trie树中间被标记的点叫做危险结点。
在AC自动机中不断遍历,如果遇到危险结点,就是不合法的
考虑如何遍历,这里需要用到AC自动机建立失败指针的一个技巧:
若当前结点无儿子结点,把失败指针处的儿子拿过来。
如果当前结点有儿子结点,把失败指针处的危险标记拿过来,详细见代码。
这样就方便了后面找后继结点。
void buildfail() { //Bfs构造Fail指针
queue<int>Q;
Q.push(root);
while(!Q.empty()) {
int Now=Q.front();
Q.pop();
for(int i=0; iint Next=tree[Now].child[i];
if(Next==0) { //儿子不存在
if(tree[tree[Now].fail].child[i])tree[Now].child[i]=tree[tree[Now].fail].child[i];
else tree[Now].child[i]=root;
continue;
}
Q.push(Next);
int fatherfail=tree[Now].fail; //父亲的失败指针
while(fatherfail&&!tree[fatherfail].child[i])fatherfail=tree[fatherfail].fail; //寻找可退回点
if(fatherfail)tree[Next].fail=tree[fatherfail].child[i]; //如果存在满足条件的点则设置失败指针
else tree[Next].fail=root; //否则指回root
tree[Next].flag|=tree[tree[Next].fail].flag;
}
}
}
有了这些基础,我们就可以动规了,f[i,j]表示从root->j的链上,长度为i的句子中的可用数目。
ans= ∑cnti=1f[m,i]
初始化f[0,1]=1
有读者就要问了,如果可用字符不在AC自动机里怎么统计呢?没有关系,如果没有的也会因为失败指针累积到root去。
此题似乎完美的解决了,其实并没有,因为还要高精度
如果一直AC不了请看过来
原因:AC自动机是WA自动机
真正原因:
看看这组数据(来自poj的discuss)
32 50 10
、¥ウЖ┆q忏溴骁栝觌祉铒?
Β
驿?
馥?
癌ē?
く.?
く.铽
Β‘飒
ウq憝?
驿Γ飒
隘ルΒ°?
不保证我的输出一定正确,但你RE就一定错了,233Hash加上个100吧
23333,做完此题整个人都升华了
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
inline const int Get_Int() {
int num=0,bj=1;
char x=getchar();
while(x<'0'||x>'9') {
if(x=='-')bj=-1;
x=getchar();
}
while(x>='0'&&x<='9') {
num=num*10+x-'0';
x=getchar();
}
return num*bj;
}
struct BigInteger {
static const int BASE=10000; //高进制
static const int WIDTH=4; //高进制位数
vector<int>s;
BigInteger() {}
BigInteger(long long num) { // 构造函数
*this=num;
}
//赋值
BigInteger operator = (long long num) {
s.clear();
do {
s.push_back(num%BASE);
num/=BASE;
} while(num>0);
return *this;
}
//+
BigInteger operator + (BigInteger& b) {
BigInteger c;
c.s.resize(max(s.size(),b.s.size())+1);
for(int i=0; i1; i++) {
int tmp1,tmp2;
if(i>=s.size())tmp1=0;
else tmp1=s[i];
if(i>=b.s.size())tmp2=0;
else tmp2=b.s[i];
c.s[i]=tmp1+tmp2;
}
for(int i=0; i1; i++) {
c.s[i+1]+=c.s[i]/BASE;
c.s[i]%=BASE;
}
while(c.s.back()==0&&c.s.size()>1)c.s.pop_back();
return c;
}
void operator += (BigInteger& b) {
*this=*this+b;
}
};
ostream& operator << (ostream& output,const BigInteger& x) {
output<for(int i=x.s.size()-2; i>=0; i--) {
char buf[20];
sprintf(buf,"%04d",x.s[i]);
for(int j=0; j<strlen(buf); j++)output<return output;
}
const int maxn=5000;
struct Tree {
int child[305],fail,flag; //fail失败指针
void clear() {
memset(child,0,sizeof(child));
fail=0;
flag=0;
}
};
int root=1,n,m,p,Index[5005];
struct Aho_Corasick_Automaton { //AC自动机
int cnt;
Tree tree[maxn];
void init() {
cnt=1;
memset(tree,0,sizeof(tree));
}
void insert(char s[]) {
int now=root,len=strlen(s);
for(int i=0; iint j=Index[s[i]+100];
if(!tree[now].child[j]) {
tree[++cnt].clear();
tree[now].child[j]=cnt;
}
now=tree[now].child[j];
}
tree[now].flag=1;
}
void buildfail() { //Bfs构造Fail指针
queue<int>Q;
Q.push(root);
while(!Q.empty()) {
int Now=Q.front();
Q.pop();
for(int i=0; iint Next=tree[Now].child[i];
if(Next==0) { //儿子不存在
if(tree[tree[Now].fail].child[i])tree[Now].child[i]=tree[tree[Now].fail].child[i];
else tree[Now].child[i]=root;
continue;
}
Q.push(Next);
int fatherfail=tree[Now].fail; //父亲的失败指针
while(fatherfail&&!tree[fatherfail].child[i])fatherfail=tree[fatherfail].fail; //寻找可退回点
if(fatherfail)tree[Next].fail=tree[fatherfail].child[i]; //如果存在满足条件的点则设置失败指针
else tree[Next].fail=root; //否则指回root
tree[Next].flag|=tree[tree[Next].fail].flag;
}
}
}
};
Aho_Corasick_Automaton ac;
BigInteger f[105][305],ans=0;
char Word[2005];
int main() {
scanf("%d%d%d%s",&n,&m,&p,&Word);
for(int i=0; i100]=i;
ac.init();
for(int i=1; i<=p; i++) {
char s[2005];
scanf("%s",&s);
ac.insert(s);
}
ac.buildfail();
f[0][1]=1;
for(int i=1; i<=m; i++) {
for(int j=1; j<=ac.cnt; j++) {
if(ac.tree[j].flag)continue;
for(int k=0; kif(ac.tree[ac.tree[j].child[k]].flag)continue;
f[i][ac.tree[j].child[k]]+=f[i-1][j];
}
}
}
for(int i=1; i<=ac.cnt; i++)
if(!ac.tree[i].flag)ans+=f[m][i];
cout<return 0;
}