hdu 2234 考研路茫茫——单词情结
求长度不超过l的串中,包含一些单词的串的个数
首先构造自动机,然后把每个包含了终结态的状态都置为终结态,比如abc b,那么ab,abc,b都是终结态,
然后构造矩阵A,Ax就是长度为一时得到的结果A^n为长度为n结果
其中x=[0,1,.........0]第一个表示终结状态,第二个表示起始状态,最后一个表示为长度小于当前长度的终结态数量之和(终结态表示这个串包含了至少一个单词)
接着构造A
对自动机上任意一个状态转移u->v如果u是终结态就不转移,否则A[v][u]++,
然后就用矩阵快速幂来求解A^n
最后的答案就是A[0][1]+A[s][1] ==== 因为只有x[1] =1其他=0,表示当前长度终结态数量+小于当前长度终结态数量 = 最后答案
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define maxn 40
int child[maxn][26],fail[maxn],state[maxn],flag[maxn];
int cnt;
void init(){
memset(child,0,sizeof(child));
memset(flag, 0,sizeof(flag));
memset(fail, 0,sizeof(fail));
cnt = 2;
for(int i = 0;i < 26;i++)
child[0][i] = 1;
}
char word[maxn];
void add(int u,int i){
int x = word[i]-'a';
if(word[i]=='\0'){
flag[u] = 1;
return ;
}
if(child[u][x]==0)child[u][x] = cnt++;
add(child[u][x],i+1);
}
#define ll unsigned __int64
int queue[100];
void build(){
int front=0,rear=0;
queue[front++] = 1;
while(front != rear){
int u = queue[rear++];
flag[u] |= flag[fail[u]];
for(int i = 0;i < 26;i++){
if(child[u][i]==0) child[u][i] = child[fail[u]][i];
else {
int v = child[u][i];
fail[v] = child[fail[u]][i];
queue[front++] = v;
}
}
}
}
struct Matrix{
ll res[30][30];
ll n;
void init(){
memset(res,0,sizeof(res));
}
};
Matrix operator*(Matrix a,Matrix b){
Matrix c;
c.init();
c.n = a.n;
for(int i = 0;i < a.n;i++){
for(int j = 0;j < a.n ;j++)
for(int k = 0;k < a.n ;k++)
c.res[i][j]+=a.res[i][k]*b.res[k][j];
}
return c;
}
int main(){
ll n,l;
while(cin>>n>>l){
init();
for(int i = 0;i < n;i++){
cin>>word;
add(1,0);
}
build();
int s = 1;
for(int i = 1;i < cnt; i++)
if(flag[i]==0)state[i]=s++;
else state[i] = 0;
Matrix a;
a.init();
for(int i = 1;i < cnt; i++){
for(int j = 0;j < 26;j++){
int u = state[child[i][j]];
int v = state[i];
if(v == 0 ) continue;
a.res[u][v]++;
}
}
a.res[0][0] = 26;
a.res[s][0] = 1;
a.res[s][s] = 1;
Matrix ans;
ans.init();
ans.n = a.n = s+1;
for(int i = 0;i < 30;i++) ans.res[i][i] = 1;
while(l){
if(l&1) ans=a*ans;
a=a*a;
l/=2;
}
ll f = ans.res[s][1]+ans.res[0][1];
printf("%I64u\n",f);
}
return 0;
}
hdu 2896 病毒侵袭
求一个串包含哪些字串,根据题目意思的话,就不用想太多了,不会有重叠情况的,
比如abc bc这种的。
解题:把字串建立ac自动机,是单词的终结态就标记为该单词的标号,
然后对于每个串走一遍自动机,如果这个标号没有出现过就加入ans
由于ans的大小<3 就直接判定就行了
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<set>
using namespace std;
#define maxn 100007
struct Node{
int child[128];
int flag;
Node*fail;
};
Node node[maxn];
int cnt;
int newNode(){
memset(node[cnt].child,-1,sizeof(node[cnt].child));
node[cnt].flag = 0;
return cnt++;
}
vector<int>ans;
char word[maxn];
int super,root;
void insert(int u,int p,int flag){
if(word[p] == '\0'){
node[u].flag = flag;
return ;
}
int l = word[p];
if(node[u].child[l] == -1){
node[u].child[l] = newNode();
}
insert(node[u].child[l],p+1,flag);
}
int queue[maxn];
void build(){
for(int i = 0;i < 128;i++)
node[super].child[i] = root;
node[root].fail = &node[super];
int front = 0,rear=0;
queue[front++] = root;
while(front > rear){
int u = queue[rear++];
for(int i = 0;i < 128;i++){
if(node[u].child[i] == -1){
node[u].child[i] = node[u].fail->child[i];
}
else {
node[node[u].child[i]].fail = &node[node[u].fail->child[i]];
queue[front++] = node[u].child[i];
}
}
}
}
void find(){
int len=strlen(word);
int u = root;
for(int i = 0;i < len; i++){
u = node[u].child[word[i]];
if(node[u].flag != 0){
int flag = 1;
for(int i = 0;i < ans.size() ;i++)
if(ans[i] == node[u].flag) flag = 0;
if(flag == 1) ans.push_back(node[u].flag);
}
if(ans.size() == 4)return ;
}
}
int main(){
int total,n;
while(scanf("%d",&n)!=EOF){
cnt = 0;
super = newNode();
root = newNode();
for(int i = 1;i <= n;i++){
scanf("%s",word);
insert(root,0,i);
}
build();
scanf("%d",&n);
int total = 0;
for(int i = 1;i <= n;i++){
scanf("%s",word);
ans.clear();
ans.push_back(0);
find();
if(ans.size() > 1){
sort(ans.begin(),ans.end());
printf("web %d:",i);
for(int j = 1;j < ans.size();j++)
printf(" %d",ans[j]);
printf("\n");
total++;
}
}
printf("total: %d\n",total);
}
return 0;
}
hdu 2222 Keywords Search
求一个串包含几个单词,解法:构建ac自动机是单词结尾的标记一下
然后走ac自动机,走到一个状态访问fail节点,如果mark!=-就加上mark,然后mark=-1
这样经过的状态就不会重复计算了,
#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
using namespace std;
struct Node{
Node*ch[26],*fail;
int mark,id;
}word[600001];
int cnt;
Node*newNode(){
memset(&word[cnt],0,sizeof(word[0]));
word[cnt].id = cnt;
return &word[cnt++];
}
void insert(Node*root,char*s){
if(*s==0){root->mark++;return;}
int id = *s-'a';
if(root->ch[id] == 0) root->ch[id] = newNode();
insert(root->ch[id],s+1);
}
Node *super,*root;
void build(Node*root){
for(int i = 0;i < 26;i++)
super->ch[i] = root;
root->fail = super;
queue<Node*>Q;
Q.push(root);
while(!Q.empty()){
Node* q = Q.front();
Q.pop();
for(int i = 0;i < 26; i++){
if(q->ch[i] != NULL){
Q.push(q->ch[i]);
q->ch[i]->fail = q->fail->ch[i];
//cout<<(char)(i+'a')<<endl;
}
else q->ch[i] = q->fail->ch[i];
}
}
}
int autofind(char *s){
int ans = 0,id;
Node*rt = root,*p;
while(*s){
id = *s - 'a';
if(rt->ch[id] != root) rt = rt->ch[id];
else{
while(rt != root && rt->ch[id] == root)
rt = rt->fail;
}
p = rt;
while(p != root && p->mark != -1){
ans += p->mark;
p->mark = -1;
p = p->fail;
}
s++;
}
return ans;
}
char a[1000009];
int main(){
int t,n;
scanf("%d",&t);
while(t--){
cnt = 0;
super = newNode();
root = newNode();
scanf("%d",&n);
while(n--){
scanf("%s",a);
insert(root,a);
}
build(root);
scanf("%s",a);
int ans = autofind(a);
printf("%d\n",ans);
}
return 0;
}