AC自动机就是一种在 Trie树上的kmp,用于多模式串的匹配及对多模式串限制的dp。初始时将所有模式串放进Trie树中,然后在Trie树上构建next数组和fail数组。通过next可以进行转移,通过fail可以找到所有具有相同后缀的模式串。有了next数组,Trie树可以看成一个图。在这个图上可以进行各种dp,一般题目上规定了有很多字符串的限制的时候,应该就要想到AC自动机。
HDU-2222
复习AC自动机。
AC自动机是将多个模式串构建成一颗Trie,然后再Trie上构建fail指针。fail指针指向下一个后缀相同的串,复杂度是 O(n) 。
#include
using namespace std;
struct Trie
{
int L,root,nxt[500007][26],end[500007],fail[500007];
int newnode()
{
for(int i=0;i<26;++i)
nxt[L][i]=-1;
end[L]=0;
return L++;
}
void init()
{
L=0;
root=newnode();
}
void insert(char buf[])
{
int len=strlen(buf);
int now=root;
for(int i=0;iif(nxt[now][buf[i]-'a']==-1)
nxt[now][buf[i]-'a']=newnode();
now=nxt[now][buf[i]-'a'];
}
++end[now];
}
void build()
{
queue<int> q;
fail[root]=root;
for(int i=0;i<26;++i)
{
if(nxt[root][i]==-1)
nxt[root][i]=root;
else
{
fail[nxt[root][i]]=root;
q.push(nxt[root][i]);
}
}
while(!q.empty())
{
int now=q.front();
q.pop();
for(int i=0;i<26;++i)
{
if(nxt[now][i]==-1)
nxt[now][i]=nxt[fail[now]][i];
else
{
fail[nxt[now][i]]=nxt[fail[now]][i];
q.push(nxt[now][i]);
}
}
}
}
int query(char buf[])
{
int len=strlen(buf);
int tmp,now=root,res=0;
for(int i=0;i'a'];
tmp=now;
while(tmp!=root)
{
res+=end[tmp];
end[tmp]=0;
tmp=fail[tmp];
}
}
return res;
}
};
Trie t;
char s[1000007];
char pt[57];
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
int n;
scanf("%d",&n);
t.init();
for(int i=0;iscanf("%s",pt);
t.insert(pt);
}
t.build();
scanf("%s",s);
printf("%d\n",t.query(s));
}
return 0;
}
HDU - 2896
set
维护不同的编号
#include
using namespace std;
struct Trie
{
int nxt[100007][128],L,root,fail[100007],end[100007];
int newnode()
{
for(int i=0;i<128;++i)
nxt[L][i]=-1;
end[L]=0;
return L++;
}
void init()
{
L=0;
root=newnode();
}
void insert(char buf[],int id)
{
int len=strlen(buf);
int now=root;
for(int i=0;iif(nxt[now][buf[i]]==-1)
nxt[now][buf[i]]=newnode();
now=nxt[now][buf[i]];
}
end[now]=id;
}
void build()
{
queue<int> q;
for(int i=0;i<128;++i)
{
if(nxt[root][i]==-1)
nxt[root][i]=root;
else
{
fail[nxt[root][i]]=root;
q.push(nxt[root][i]);
}
}
while(!q.empty())
{
int now=q.front();q.pop();
for(int i=0;i<128;++i)
{
if(nxt[now][i]==-1)
nxt[now][i]=nxt[fail[now]][i];
else
{
fail[nxt[now][i]]=nxt[fail[now]][i];
q.push(nxt[now][i]);
}
}
}
}
set<int> query(char buf[])
{
int len=strlen(buf);
int now=root,tmp;
set<int> res;
for(int i=0;iwhile(tmp!=root)
{
if(end[tmp])
res.insert(end[tmp]);
tmp=fail[tmp];
}
}
return res;
}
};
Trie t;
char pt[207];
char s[10007];
int main()
{
int n;
while(~scanf("%d",&n))
{
t.init();
for(int i=1;i<=n;++i)
{
scanf("%s",pt);
t.insert(pt,i);
}
t.build();
int m,ans=0;
scanf("%d",&m);
for(int i=1;i<=m;++i)
{
scanf("%s",s);
auto res=t.query(s);
if(res.size())
{
++ans;
printf("web %d:",i);
for(auto k : res) printf(" %d",k);
puts("");
}
}
printf("total: %d\n",ans);
}
return 0;
}
HDU-3065
计数。。
#include
using namespace std;
int cnt[1007];
struct Trie
{
int L,root,nxt[50007][128],fail[50007],end[50007];
int newnode()
{
for(int i=0;i<128;++i)
nxt[L][i]=-1;
end[L]=0;
return L++;
}
void init()
{
L=0;
root=newnode();
}
void insert(string & buf , int id)
{
int now=root,len=buf.length();
for(int i=0;iif(nxt[now][buf[i]]==-1)
nxt[now][buf[i]]=newnode();
now=nxt[now][buf[i]];
}
end[now]=id;
}
void build()
{
queue<int> q;
for(int i=0;i<128;++i)
{
if(nxt[root][i]==-1)
nxt[root][i]=root;
else
{
fail[nxt[root][i]]=root;
q.push(nxt[root][i]);
}
}
while(!q.empty())
{
int now=q.front();q.pop();
for(int i=0;i<128;++i)
{
if(nxt[now][i]==-1)
nxt[now][i]=nxt[fail[now]][i];
else
{
fail[nxt[now][i]]=nxt[fail[now]][i];
q.push(nxt[now][i]);
}
}
}
}
void query(string & buf)
{
int len=buf.length(),now=root,tmp;
for(int i=0;iwhile(tmp!=root)
{
++cnt[end[tmp]];
tmp=fail[tmp];
}
}
}
};
string pt[1007],s;
Trie t;
int main ()
{
ios::sync_with_stdio(false);
int n;
while(cin >> n)
{
t.init();
memset(cnt,0,sizeof(cnt));
for(int i=1;i<=n;++i)
{
cin >> pt[i];
t.insert(pt[i],i);
}
t.build();
cin >> s;
t.query(s);
for(int i=1;i<=n;++i)
if(cnt[i])
cout << pt[i] << ": " << cnt[i] << '\n';
}
return 0;
}
ZOJ - 3430
先base64解码后再搞。。有个RE点就是解码后的字符是uchar的。
#include
using namespace std;
string b64="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
int f[128];
int buf[100000];
int str[100000],strl;
void decode(string &s)
{
int len=0;
for(int i=0;iif(s[i]=='=')
{
len-=(s.length()-i+1)*2;
break;
}
for(int j=0;j<6;++j)
buf[len+j]=(f[s[i]]>>(6-j-1))&1;
len+=6;
}
strl=0;
for(int i=0;i8)
{
int k=0;
for(int j=0;j<8;++j)
k<<=1,k|=buf[i+j];
str[strl++]=k;
}
}
struct Trie
{
int L,root,nxt[40000][256],fail[40000],end[40000],vis[40000];
int newnode()
{
for(int i=0;i<256;++i)
nxt[L][i]=-1;
end[L]=vis[L]=0;
return L++;
}
void init()
{
L=0;
root=newnode();
}
void insert(int buf[],int len)
{
int now=root;
for(int i=0;iif(nxt[now][buf[i]]==-1)
nxt[now][buf[i]]=newnode();
now=nxt[now][buf[i]];
}
++end[now];
}
void build()
{
queue<int> q;
for(int i=0;i<256;++i)
{
if(nxt[root][i]==-1)
nxt[root][i]=root;
else
{
fail[nxt[root][i]]=root;
q.push(nxt[root][i]);
}
}
while(!q.empty())
{
int now=q.front();q.pop();
for(int i=0;i<256;++i)
{
if(nxt[now][i]==-1)
nxt[now][i]=nxt[fail[now]][i];
else
{
fail[nxt[now][i]]=nxt[fail[now]][i];
q.push(nxt[now][i]);
}
}
}
}
int query(int buf[],int len ,int id)
{
int now=root,tmp,res=0;
for(int i=0;iwhile(tmp!=root)
{
if(vis[tmp]!=id)
res+=end[tmp];
vis[tmp]=id;
tmp=fail[tmp];
}
}
return res;
}
};
Trie t;
int main()
{
// ios::sync_with_stdio(false);
for(int i=0;i<64;++i)
f[b64[i]]=i;
int n,m;
while(cin >> n )
{
string s;
t.init();
for(int i=1;i<=n;++i)
{
cin >> s;
decode(s);
t.insert(str,strl);
}
t.build();
cin >> m;
for(int i=1;i<=m;++i)
{
cin >> s;
decode(s);
cout << t.query(str,strl,i) << '\n';
}
cout << '\n';
}
return 0;
}
POJ - 2778
考虑将所有串建AC自动机,然后Trie树上的next代表邻接表。
如果该点的end
不是1,即不是某个串的终止点,那么这个点是可以被转移的,转移方程为
然后通过快速幂加速这个dp就行了。
#include
#include
#include
using namespace std;
typedef long long ll;
const ll mod = 100000;
struct Matrix
{
ll a[57][57];
int n;
Matrix(int _n)
{
n=_n;
memset(a,0,sizeof(a));
}
ll* operator [](int index) { return a[index]; }
Matrix operator * (Matrix &b)
{
Matrix c(n);
for(int i=0;ifor(int k=0;kif(a[i][k])
for(int j=0;jreturn c;
}
};
Matrix powm(Matrix a, int b)
{
Matrix c(a.n);
for(int i=0;ifor(int j=0;jwhile(b)
{
if(b&1) c=c*a;
a=a*a;
b>>=1;
}
return c;
}
struct Trie
{
int nxt[57][4],fail[57],end[57],L,root;
int newnode()
{
for(int i=0;i<4;++i)
nxt[L][i]=-1;
end[L]=0;
return L++;
}
void init()
{
L=0;
root=newnode();
}
int f(char ch)
{
if(ch=='A') return 0;
else if(ch=='C') return 1;
else if(ch=='G') return 2;
else if(ch=='T') return 3;
}
void insert(char buf[])
{
int len=strlen(buf),now=root;
for(int i=0;iif(nxt[now][f(buf[i])]==-1)
nxt[now][f(buf[i])]=newnode();
now=nxt[now][f(buf[i])];
}
end[now]=1;
}
void build()
{
queue<int> q;
for(int i=0;i<4;++i)
{
if(nxt[root][i]==-1)
nxt[root][i]=root;
else
{
fail[nxt[root][i]]=root;
q.push(nxt[root][i]);
}
}
while(!q.empty())
{
int now=q.front();q.pop();
if(end[fail[now]]) end[now]=1;
for(int i=0;i<4;++i)
{
if(nxt[now][i]==-1)
nxt[now][i]=nxt[fail[now]][i];
else
{
fail[nxt[now][i]]=nxt[fail[now]][i];
q.push(nxt[now][i]);
}
}
}
}
ll query(int b)
{
Matrix a(L);
for(int i=0;ifor(int j=0;j<4;++j)
if(!end[nxt[i][j]])
++a[nxt[i][j]][i];
a=powm(a,b);
ll res=0;
for(int i=0;i0])%mod;
return res;
}
}t;
char pt[50];
int main ()
{
int n,m;
while(~scanf("%d%d",&n,&m))
{
t.init();
for(int i=0;iscanf("%s",pt);
t.insert(pt);
}
t.build();
printf("%I64d\n",t.query(m));
}
return 0;
}
HDU - 2243
快速幂的时候再维护一个求和变量就行了。。其他跟上题差不多。
#include
using namespace std;
typedef unsigned long long ll;
struct Matrix
{
ll a[57][57];
int n;
Matrix(int _n)
{
n=_n;
for(int i=0;ifor(int j=0;j0;
}
ll* operator [](int index) { return a[index]; }
Matrix operator * (Matrix &b)
{
Matrix c(n);
for(int i=0;ifor(int k=0;kif(a[i][k])
for(int j=0;jreturn c;
}
};
Matrix powm(Matrix &a, ll b)
{
Matrix c(a.n);
for(int i=0;ifor(int j=0;jwhile(b)
{
if(b&1) c=c*a;
a=a*a;
b>>=1;
}
return c;
}
struct Trie
{
int nxt[57][26],fail[57],end[57],L,root;
int newnode()
{
for(int i=0;i<26;++i)
nxt[L][i]=-1;
end[L]=0;
return L++;
}
void init()
{
L=0;
root=newnode();
}
void insert(char buf[])
{
int len=strlen(buf),now=root;
for(int i=0;iif(nxt[now][buf[i]-'a']==-1)
nxt[now][buf[i]-'a']=newnode();
now=nxt[now][buf[i]-'a'];
}
end[now]=1;
}
void build()
{
queue<int> q;
for(int i=0;i<26;++i)
{
if(nxt[root][i]==-1)
nxt[root][i]=root;
else
{
fail[nxt[root][i]]=root;
q.push(nxt[root][i]);
}
}
while(!q.empty())
{
int now=q.front();q.pop();
if(end[fail[now]]) end[now]=1;
for(int i=0;i<26;++i)
{
if(nxt[now][i]==-1)
nxt[now][i]=nxt[fail[now]][i];
else
{
fail[nxt[now][i]]=nxt[fail[now]][i];
q.push(nxt[now][i]);
}
}
}
}
ll query(ll b)
{
Matrix a(L+1);
for(int i=0;ifor(int j=0;j<26;++j)
if(!end[nxt[i][j]])
++a[nxt[i][j]][i];
for(int i=0;i<=L;++i) a[L][i]=1;
a=powm(a,b+1);
return a[L][0]-1;
}
}t;
char pt[50];
ll getAll(ll b)
{
Matrix c(2);
c[0][0]=c[0][1]=1;
c[1][1]=26;
c=powm(c,b);
return c[0][1]*26;
}
int main ()
{
int n;ll m;
ios::sync_with_stdio(false);
while(cin >> n >> m)
{
t.init();
for(int i=0;icin >> pt;
t.insert(pt);
}
t.build();
cout << getAll(m)-t.query(m) << '\n';
}
return 0;
}
HDU - 2825
dp[i][j] 表示第 i 个结点状态为 j 时的数量,然后转移就行。。。
#include
using namespace std;
const int mod = 20090717;
struct Trie
{
int nxt[107][26],end[107],L,root,fail[107],dp[107][1024],tmp[107][1024];
int newnode()
{
for(int i=0;i<26;++i)
nxt[L][i]=-1;
end[L]=0;
return L++;
}
void init()
{
L=0;
root=newnode();
}
void insert(char buf[],int id)
{
int len=strlen(buf),now=root;
for(int i=0;iif(nxt[now][buf[i]-'a']==-1)
nxt[now][buf[i]-'a'] = newnode();
now=nxt[now][buf[i]-'a'];
}
end[now]|=(1<void build()
{
queue<int> q;
for(int i=0;i<26;++i)
{
if(nxt[root][i]==-1)
nxt[root][i]=root;
else
{
fail[nxt[root][i]]=root;
q.push(nxt[root][i]);
}
}
while(!q.empty())
{
int now=q.front();q.pop();
end[now]|=end[fail[now]];
for(int i=0;i<26;++i)
{
if(nxt[now][i]==-1)
nxt[now][i]=nxt[fail[now]][i];
else
{
fail[nxt[now][i]]=nxt[fail[now]][i];
q.push(nxt[now][i]);
}
}
}
}
int solve(int n,int m,int c)
{
for(int j=0;jfor(int s=0;s<(1<0;
dp[0][0]=1;
for(int i=0;ifor(int j=0;jfor(int s=0;s<(1<0;
for(int j=0;jfor(int s=0;s<(1<if(dp[j][s])
{
for(int k=0;k<26;++k)
{
int nx=nxt[j][k];
tmp[nx][end[nx]|s]=(tmp[nx][end[nx]|s]+dp[j][s])%mod;
}
}
for(int j=0;jfor(int s=0;s<(1<int res=0;
for(int i=0;ifor(int j=0;j<(1<if(__builtin_popcount(j)>=c)
res=(res+dp[i][j])%mod;
return res;
}
}t;
char buf[20];
int main()
{
int n,m,k;
while(~scanf("%d%d%d",&n,&m,&k))
{
if(n==0&&m==0&&k==0) break;
t.init();
for(int i=0;iscanf("%s",buf);
t.insert(buf,i);
}
t.build();
printf("%d\n",t.solve(n,m,k));
}
return 0;
}
ZOJ - 3228
AC自动机常见的计数字符串题,不过有不能重叠的限制。可以考虑维护每种模式串上一次出现的位置 last
,若当前位置减上一次出现位置大于等于长度的话,这部分可以计数。
#include
using namespace std;
const int N=1e5+7;
map<string,int> mp;
string s;
int id[N],len[N],last[N],c1[N],c2[N],op[N];
struct Trie
{
int L,root,nxt[N*6][26],fail[N*6],end[N*6];
int newnode()
{
for(int i=0;i<26;++i) nxt[L][i]=-1;
end[L]=0;
return L++;
}
void init()
{
L=0;
root=newnode();
}
void insert(string & buf, int id)
{
int now=root;
for(int i=0;iif(nxt[now][buf[i]-'a']==-1)
nxt[now][buf[i]-'a']=newnode();
now = nxt[now][buf[i]-'a'];
}
end[now]=id;
}
void build()
{
queue<int> q;
for(int i=0;i<26;++i)
{
if(nxt[root][i]==-1)
nxt[root][i]=root;
else
{
fail[nxt[root][i]]=root;
q.push(nxt[root][i]);
}
}
while(!q.empty())
{
int now=q.front();q.pop();
for(int i=0;i<26;++i)
{
if(nxt[now][i]==-1)
nxt[now][i]=nxt[fail[now]][i];
else
{
fail[nxt[now][i]]=nxt[fail[now]][i];
q.push(nxt[now][i]);
}
}
}
}
void query(string &buf)
{
int now=root,tmp;
for(int i=0;i'a'];
tmp=now;
while(tmp!=root)
{
if(end[tmp])
{
if(i-last[end[tmp]]>=len[end[tmp]])
++c2[end[tmp]],last[end[tmp]]=i;
++c1[end[tmp]];
}
tmp=fail[tmp];
}
}
}
}t;
int main()
{
ios::sync_with_stdio(false);
int kase=1;
string buf;
while(cin >> s)
{
int n,cur=0;
cin >> n;
mp.clear();
t.init();
for(int i=0;icin >> op[i] >> buf;
if(!mp.count(buf))
{
mp[buf]=++cur;
len[cur]=buf.length();
last[cur]=-10000;
c1[cur]=c2[cur]=0;
t.insert(buf,cur);
}
id[i]=mp[buf];
}
t.build();
t.query(s);
cout << "Case " << (kase++) << '\n';
for(int i=0;iif(op[i]) cout << c2[id[i]] << '\n';
else cout << c1[id[i]] << '\n';
}
cout << '\n' ;
}
return 0;
}
HDU - 3341
假如只有A和C这两个字母的话,那么很简单,直接状压转移就行,不过这里有4个字母,可以考虑根据每种字母的数量将其哈希成一种状态。状态数最大是 114=14641 。然后dp就行了。
#include
using namespace std;
int hs[41][41][41][41],dp[507][15000];
inline int Max(int a,int b) { return a > b ? a : b; }
struct Trie
{
int L,root,nxt[507][4],fail[507],end[507];
int newnode()
{
for(int i=0;i<4;++i)
nxt[L][i]=-1;
end[L]=0;
return L++;
}
void init()
{
L=0;
root=newnode();
}
int f(char ch)
{
if(ch=='A') return 0;
else if(ch=='G') return 1;
else if(ch=='C') return 2;
else return 3;
}
void insert(char buf[])
{
int now=root,len=strlen(buf);
for(int i=0;iif(nxt[now][f(buf[i])]==-1)
nxt[now][f(buf[i])]=newnode();
now = nxt[now][f(buf[i])];
}
++end[now];
}
void build()
{
queue<int> q;
for(int i=0;i<4;++i)
{
if(nxt[root][i]==-1)
nxt[root][i]=root;
else
{
fail[nxt[root][i]]=root;
q.push(nxt[root][i]);
}
}
while(!q.empty())
{
int now=q.front();q.pop();
end[now]+=end[fail[now]];
for(int i=0;i<4;++i)
{
if(nxt[now][i]==-1)
nxt[now][i]=nxt[fail[now]][i];
else
{
fail[nxt[now][i]]=nxt[fail[now]][i];
q.push(nxt[now][i]);
}
}
}
}
int query(char buf[])
{
int cnt[4],len=strlen(buf);
memset(cnt,0,sizeof(cnt));
for(int i=0;iint all=0;
for(int a=0;a<=cnt[0];++a)
for(int b=0;b<=cnt[1];++b)
for(int c=0;c<=cnt[2];++c)
for(int d=0;d<=cnt[3];++d)
hs[a][b][c][d]=all++;
for(int i=0;ifor(int j=0;j1;
dp[0][0]=1;
for(int a=0;a<=cnt[0];++a)
for(int b=0;b<=cnt[1];++b)
for(int c=0;c<=cnt[2];++c)
for(int d=0;d<=cnt[3];++d)
for(int i=0;iif(dp[i][hs[a][b][c][d]]!=-1)
{
int id=hs[a][b][c][d];
if(a+1<=cnt[0]) dp[nxt[i][0]][hs[a+1][b][c][d]]=Max(dp[nxt[i][0]][hs[a+1][b][c][d]],dp[i][id]+end[nxt[i][0]]);
if(b+1<=cnt[1]) dp[nxt[i][1]][hs[a][b+1][c][d]]=Max(dp[nxt[i][1]][hs[a][b+1][c][d]],dp[i][id]+end[nxt[i][1]]);
if(c+1<=cnt[2]) dp[nxt[i][2]][hs[a][b][c+1][d]]=Max(dp[nxt[i][2]][hs[a][b][c+1][d]],dp[i][id]+end[nxt[i][2]]);
if(d+1<=cnt[3]) dp[nxt[i][3]][hs[a][b][c][d+1]]=Max(dp[nxt[i][3]][hs[a][b][c][d+1]],dp[i][id]+end[nxt[i][3]]);
}
int res=0;
for(int i=0;i1]);
return res-1;
}
}t;
char buf[50];
int main ()
{
int n,kase=1;
while(~scanf("%d",&n))
{
if(n==0) break;
t.init();
for(int i=0;iscanf("%s",buf);
t.insert(buf);
}
t.build();
scanf("%s",buf);
printf("Case %d: %d\n",kase++,t.query(buf));
}
return 0;
}
HDU - 3247
首先求出各个资源串末尾之间的距离,要求不能有病毒串,所以走向病毒串的边不能走。因为各边为 1 ,因此跑一遍 bfs
就行了。然后作一遍状压dp,dp[i][j] 表示状态为 j ,且最后遇到的串是 j 时的消耗的最小字母数。
#include
using namespace std;
const int N=60007,INF=0x3f3f3f3f;
int d[10][10],dis[N],tail[10];
int Q[N];
struct Queue
{
int head,tail;
Queue() : head(0) , tail(0) {}
bool empty(){return head==tail;}
void push(int a){Q[tail++]=a;}
void pop() { ++head; }
int front() { return Q[head]; }
};
struct Trie
{
int nxt[N][2],fail[N],end[N],rs[N],L,root,n;
bool vis[N];
int newnode()
{
nxt[L][0]=nxt[L][1]=-1;
end[L]=0;
rs[L]=0;
return L++;
}
void init()
{
L=0;
root=newnode();
}
void insert_rs(char buf[],int id)
{
int now=root,len=strlen(buf);
for(int i=0;iif(nxt[now][buf[i]-'0']==-1)
nxt[now][buf[i]-'0']=newnode();
now=nxt[now][buf[i]-'0'];
}
rs[now]|=(1<void insert(char buf[])
{
int now=root,len=strlen(buf);
for(int i=0;iif(nxt[now][buf[i]-'0']==-1)
nxt[now][buf[i]-'0']=newnode();
now=nxt[now][buf[i]-'0'];
}
end[now]=1;
}
void build()
{
Queue q;
for(int i=0;i<2;++i)
{
if(nxt[root][i]==-1)
nxt[root][i]=root;
else
{
fail[nxt[root][i]]=root;
q.push(nxt[root][i]);
}
}
while(!q.empty())
{
int now=q.front();q.pop();
rs[now]|=rs[fail[now]];
end[now]|=end[fail[now]];
for(int i=0;i<2;++i)
{
if(nxt[now][i]==-1)
nxt[now][i]=nxt[fail[now]][i];
else
{
fail[nxt[now][i]]=nxt[fail[now]][i];
q.push(nxt[now][i]);
}
}
}
}
vector<int> tmp;
vector<int> & pts(int k)
{
tmp.clear();
for(int i=0;iif((k>>i)&1) tmp.push_back(i);
return tmp;
}
void bfs(int st,int d[])
{
// puts("bfs");
Queue q;
for(int i=0;i0x3f3f3f3f;
memset(vis,0,sizeof(vis));
memset(dis,0x3f,sizeof(dis));
dis[st]=0;
for(int v : pts(rs[st]))
d[v]=0;
vis[st]=true;
q.push(st);
while(!q.empty())
{
int p=q.front();q.pop();
int u=nxt[p][0];
if(!end[u]&&!vis[u])
{
vis[u]=true;
dis[u]=dis[p]+1;
q.push(u);
if(rs[u])
for(int v : pts(rs[u]))
d[v]=min(dis[u],d[v]);
}
u=nxt[p][1];
if(!end[u]&&!vis[u])
{
vis[u]=true;
dis[u]=dis[p]+1;
q.push(u);
if(rs[u])
for(int v : pts(rs[u]))
d[v]=min(dis[u],d[v]);
}
}
}
}t;
char buf[50007];
int dp[10][1024];
int main()
{
int n,m;
while(~scanf("%d%d",&n,&m))
{
if(n==0 || m==0) break;
t.init();
t.n=n;
memset(dp,0x3f,sizeof(dp));
for(int i=0;iscanf("%s",buf);
t.insert_rs(buf,i);
dp[i][1<strlen(buf);
}
for(int i=0;iscanf("%s",buf);
t.insert(buf);
}
t.build();
for(int i=0;ifor(int i=1;i<(1<for(int j=0;jfor(int k=0;kif(((i>>k)&1)^1)
dp[k][i|(1<1<int ans=INF;
for(int i=0;i1<1]);
printf("%d\n",ans);
}
return 0;
}
HDU - 4758
AC自动机上dp,dp[i][j][a][s] 表示第 i 个字母匹配到了自动机上第 j 个结点,消耗了 a 个 R ,状态为 s 时的方案数。
#include
using namespace std;
const int mod = 1e9+7;
int Q[207];
struct Queue
{
int head,tail;
Queue() : head(0) , tail(0) {}
bool empty(){return head==tail;}
void push(int a){Q[tail++]=a;}
void pop() { ++head; }
int front() { return Q[head]; }
};
struct Trie
{
int nxt[207][2],end[207],fail[207],dp[203][203][103][4],root,L;
int newnode()
{
nxt[L][0]=nxt[L][1]=-1;
end[L]=0;
return L++;
}
void init()
{
L=0;
root=newnode();
}
int f(char ch){ return ch=='R' ? 0 : 1; }
void insert(char buf[],int id)
{
int now=root,len=strlen(buf);
for(int i=0;iif(nxt[now][f(buf[i])]==-1)
nxt[now][f(buf[i])]=newnode();
now=nxt[now][f(buf[i])];
}
end[now]=(1<void build()
{
Queue q;
for(int i=0;i<2;++i)
{
if(nxt[root][i]==-1)
nxt[root][i]=root;
else
{
fail[nxt[root][i]]=root;
q.push(nxt[root][i]);
}
}
while(!q.empty())
{
int now=q.front();q.pop();
end[now]|=end[fail[now]];
for(int i=0;i<2;++i)
{
if(nxt[now][i]==-1)
nxt[now][i]=nxt[fail[now]][i];
else
{
fail[nxt[now][i]]=nxt[fail[now]][i];
q.push(nxt[now][i]);
}
}
}
}
int query(int n,int m)
{
int i,j,a,s;
for(i=0;i<=n+m;++i)
for(j=0;jfor(a=0;a<=n;++a)
for(s=0;s<4;++s)
dp[i][j][a][s]=0;
dp[0][0][0][0]=1;
for(i=1;i<=n+m;++i)
for(j=0;jfor(a=0;a<=n&&afor(s=0;s<4;++s)
{
int nx=nxt[j][0];
if(a1][s|end[nx]]=(dp[i][nx][a+1][s|end[nx]]+dp[i-1][j][a][s])%mod;
nx=nxt[j][1];
if(i-1-a1][j][a][s])%mod;
}
int ans=0;
for(i=0;i3])%mod;
return ans;
}
}t;
int main ()
{
int T,n,m;
scanf("%d",&T);
char buf[207];
while(T--)
{
int n,m;
scanf("%d%d",&n,&m);
t.init();
scanf("%s",buf);
t.insert(buf,0);
scanf("%s",buf);
t.insert(buf,1);
t.build();
printf("%d\n",t.query(n,m));
}
return 0;
}
HDU - 4511
在AC自动机上dp找最短路。
dp[i][j] 表示第 i 个点在自动机上是第 j 个点的最小路径,只能向合法路径且点标号大的方向转移。因为从1开始,初始时将 dp[i][nxt[root][i]]=0 。
#include
using namespace std;
typedef double ld;
const ld INF = 1e20;
ld x[51],y[51],d[51][51];
struct Trie
{
int nxt[507][51],fail[507],end[507],L,root,n;
ld dp[51][507];
int newnode()
{
for(int i=1;i<=n;++i)
nxt[L][i]=-1;
end[L]=0;
return L++;
}
void init(int _n)
{
n=_n;
L=0;
root=newnode();
}
void insert(int buf[],int len)
{
int now=root;
for(int i=0;iif(nxt[now][buf[i]]==-1)
nxt[now][buf[i]]=newnode();
now=nxt[now][buf[i]];
}
end[now]=1;
}
void build()
{
queue<int> q;
for(int i=1;i<=n;++i)
{
if(nxt[root][i]==-1)
nxt[root][i]=root;
else
{
fail[nxt[root][i]]=root;
q.push(nxt[root][i]);
}
}
while(!q.empty())
{
int now=q.front();q.pop();
end[now]|=end[fail[now]];
for(int i=1;i<=n;++i)
{
if(nxt[now][i]==-1)
nxt[now][i]=nxt[fail[now]][i];
else
{
fail[nxt[now][i]]=nxt[fail[now]][i];
q.push(nxt[now][i]);
}
}
}
}
ld query()
{
for(int i=1;i<=n;++i)
for(int j=i+1;j<=n;++j)
d[i][j]=sqrt((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]));
for(int i=1;i<=n;++i)
for(int j=0;j1][nxt[root][1]]=0;
for(int i=1;i<=n;++i)
for(int j=0;jif(dp[i][j]!=INF)
for(int k=i+1;k<=n;++k)
if(!end[nxt[j][k]])
dp[k][nxt[j][k]]=min(dp[k][nxt[j][k]],dp[i][j]+d[i][k]);
ld ans = INF;
for(int i=0;ireturn ans;
}
}t;
int buf[51];
int main ()
{
int n,m;
while(~scanf("%d%d",&n,&m))
{
if(n==0&&m==0) break;
for(int i=1;i<=n;++i)
scanf("%lf%lf",&x[i],&y[i]);
t.init(n);
for(int i=0;iint k;
scanf("%d",&k);
for(int i=0;iscanf("%d",&buf[i]);
t.insert(buf,k);
}
t.build();
ld ans=t.query();
if(ans==INF) puts("Can not be reached!");
else printf("%.2f\n",ans);
}
return 0;
}