您的位置:首页 > 其它

AC自动机专题小结

2014-07-09 11:56 369 查看
唔.....AC自动机的资料觉得大白书应该是很不错的资料的,再推荐一篇大牛的blog
http://blog.csdn.net/niushuai666/article/details/7002823
在理解了KMP之后相信AC自动机也不会有什么太大的问题

而与KMP之间的出入也就在于,KMP中可以直接定位具体是哪一个字符(即每一个节点都是代表了某一个确认的字符)

而AC自动机是以trie树为基础建立的,因此是通过确认某一个字符的指针是否非空来确定该字符是否存在(即可以把每一个点看做一个传送门集合地)

这样会不会好理解一点呢?(个人观点仅供参考)

下面仍然是题目

【HDU 2222】Keywords Search

很基础的AC自动机了,没什么好说的

#include<cstdio>
#include<queue>
#include<cstring>
#include<algorithm>
using namespace std;
const int SIZEN=500005;
int ch[SIZEN][26],sz;
int f[SIZEN],last[SIZEN];
int val[SIZEN],ret;
char str[SIZEN<<1];
int idx(char c){
return c-'a';
}
void Insert(char s[]){
int len=strlen(s);
int rt=0;
for(int i=0;i<len;i++)
{
int t=idx(s[i]);
if(!ch[rt][t]){
memset(ch[sz],0,sizeof(ch[sz]));
ch[rt][t]=sz++;
}
rt=ch[rt][t];
}
val[rt]++;
}
void add(int j){
while(j){
ret+=val[j];
val[j]=0;
j=last[j];
}
}
void Find(char s[]){
int len=strlen(s);
int j=0;
for(int i=0;i<len;i++){
int c=idx(s[i]);
while(j&&!ch[j][c]) j=f[j];
j=ch[j][c];
if(val[j]) {add(j);}
else if(last[j]) {add(last[j]);}
}
}
void getfail(){
queue<int> q;
f[0]=0;
for(int c=0;c<26;c++){
int u=ch[0][c];
if(u) {f[u]=0;q.push(u);last[u]=0;}
}
while(!q.empty()){
int r=q.front();q.pop();
for(int c=0;c<26;c++){
int u=ch[r][c];
if(!u) continue;
q.push(u);
int v=f[r];
while(v&&!ch[v][c]) v=f[v];
f[u]=ch[v][c];
last[u]=val[f[u]]?f[u]:last[f[u]];
}
}
}
void init(){
memset(val,0,sizeof(val));
memset(ch[0],0,sizeof(ch[0]));
ret=0;
sz=1;
}
int main()
{
//freopen("data.in","r",stdin);
int i,j;
int _,n;
scanf("%d",&_);
while(_--){
init();
scanf("%d",&n);
for(i=0;i<n;i++)
{
scanf("%s",str);
Insert(str);
}
getfail();
scanf("%s",str);
Find(str);
printf("%d\n",ret);
}
return 0;
}
【HDU 2896】病毒侵袭

这道题的主要问题就是可见字符是包括空格的

因此要用gets

#include<cstdio>
#include<queue>
#include<cstring>
#include<algorithm>
using namespace std;
const int SIZEN=80005;
char str[SIZEN];
int ch[SIZEN][100],sz;
int f[SIZEN],last[SIZEN];
int val[SIZEN],flag[600];
void init(){
memset(ch,0,sizeof(ch));
memset(val,0,sizeof(val));
sz=1;
}
int idx(char c){
return c-32;
}
void Insert(char s[],int v){
int len=strlen(str);
int rt=0;
for(int i=0;i<len;i++){
int t=idx(s[i]);
if(!ch[rt][t]){
memset(ch[sz],0,sizeof(sz));
ch[rt][t]=sz++;
}
rt=ch[rt][t];
}
val[rt]=v;
}
void Insert(char s[]){
int len=strlen(s);
int rt=0;
for(int i=0;i<len;i++)
{
int t=idx(s[i]);
if(!ch[rt][t]){
memset(ch[sz],0,sizeof(ch[sz]));
ch[rt][t]=sz++;
}
rt=ch[rt][t];
}
val[rt]++;
}
void getfail(){
queue<int> q;
f[0]=0;
for(int c=0;c<100;c++){
int u=ch[0][c];
if(u) {f[u]=0;q.push(u);last[u]=0;}
}
while(!q.empty()){
int r=q.front();q.pop();
for(int c=0;c<100;c++){
int u=ch[r][c];
if(!u) {ch[r][c]=ch[f[r]][c];continue;}
q.push(u);
int v=f[r];
while(v&&!ch[v][c]) v=f[v];
f[u]=ch[v][c];
last[u]=val[f[u]]?f[u]:last[f[u]];
}
}
}
void add(int j){
while(j){
flag[val[j]]=1;
j=last[j];
}
}
void Find(char s[]){
int len=strlen(s);
int j=0;
for(int i=0;i<len;i++){
int c=idx(s[i]);
j=ch[j][c];
if(val[j]) {add(j);}
else if(last[j]) {add(last[j]);}
}
}
void output(int id){
printf("web %d: ",id);
int tflag=0;
for(int i=1;i<600;i++){
if(tflag&&flag[i]) printf(" %d",i);
else if(flag[i]){
tflag=1;
printf("%d",i);
}
}
printf("\n");
}
int main()
{
//freopen("data.in","r",stdin);
int i,j;
int n,m;
int cnt;
while(scanf("%d%*c",&n)!=EOF){
init();
for(i=0;i<n;i++){
gets(str);
Insert(str,i+1);
}
getfail();
scanf("%d%*c",&m);
cnt=0;
for(i=0;i<m;i++){
gets(str);
memset(flag,0,sizeof(flag));
Find(str);
int tt=0;
for(j=1;j<=n;j++){
if(flag[j]){
tt=1;
break;
}
}
if(tt){
cnt++;
output(i+1);
}
}
printf("total: %d\n",cnt);
}
return 0;
}
【HDU 3065】 病毒侵袭持续中

还是模版题,写这种题的时候目的只是为了理解+熟悉模版

#include<cstdio>
#include<queue>
#include<cstring>
#include<algorithm>
using namespace std;
const int SIZEN=50005;
int ch[SIZEN][26],sz;
int f[SIZEN],last[SIZEN];
int val[SIZEN],cnt[1005];
char str[2000005];
char virus[1005][55];
void init(){
memset(ch[0],0,sizeof(ch[0]));
memset(val,0,sizeof(val));
memset(cnt,0,sizeof(cnt));
sz=1;
}
int idx(char c){
return c-'A';
}
void Insert(char s[],int v){
int len=strlen(s);
int rt=0;
for(int i=0;i<len;i++){
int t=idx(str[i]);
if(!ch[rt][t]){
memset(ch[sz],0,sizeof(ch[sz]));
ch[rt][t]=sz++;
}
rt=ch[rt][t];
}
val[rt]=v;
}
void add(int j){
while(j){
cnt[val[j]]++;
j=last[j];
}
}
void Find(char s[]){
int len=strlen(s);
int j=0;
for(int i=0;i<len;i++){
if(!(str[i]<='Z'&&str[i]>='A')){
j=0;
continue;
}
int c=idx(str[i]);
while(j&&!ch[j][c]) j=f[j];
j=ch[j][c];
if(val[j]) add(j);
else if(last[j]) add(last[j]);
}
}
void getfail(){
queue<int> q;
f[0]=0;
for(int c=0;c<26;c++){
int u=ch[0][c];
if(u){f[u]=0;q.push(u);last[u]=0;}
}
while(!q.empty()){
int r=q.front();q.pop();
for(int c=0;c<26;c++){
int u=ch[r][c];
if(!u) continue;
q.push(u);
int v=f[r];
while(v&&!ch[v][c]) v=f[v];
f[u]=ch[v][c];
last[u]=val[f[u]]?f[u]:last[f[u]];
}
}
}
int main()
{
//freopen("data.in","r",stdin);
int i,j;
int n;
while(scanf("%d",&n)!=EOF){
init();
for(i=1;i<=n;i++){
scanf("%s%*c",str);
Insert(str,i);
strcpy(virus[i],str);
}
getfail();
gets(str);
Find(str);
for(i=1;i<=n;i++){
if(cnt[i]) printf("%s: %d\n",virus[i],cnt[i]);
}
}
}


【HDU 2243】单词情结

感谢wzm学长对我的悉心教导,才让我能做出第一道AC自动机+dp

这道题一看数据L有2^31-1这么多,应该想到用矩阵加速

而需要求得是所有小于L满足条件的式子,因此需要增加一维来保存总的结果

至于矩阵的构造沿着AC自动机跑一遍就知道了

#include<cstdio>
#include<queue>
#include<cstring>
#include<algorithm>
using namespace std;
typedef unsigned long long ULL;
typedef long long LL;
const int SIZEN=40;
int ch[SIZEN][26],sz;
int f[SIZEN],val[SIZEN];
int cc[SIZEN],n;
char str[SIZEN];
struct Mat{
ULL mat[SIZEN][SIZEN];
void init(){
memset(mat,0,sizeof(mat));
}
void output(){
for(int i=0;i<=sz;i++){
for(int j=0;j<=sz;j++) printf("%2d ",mat[i][j]);
printf("\n");
}
}
void getE(){
memset(mat,0,sizeof(mat));
for(int i=0;i<=sz;i++)
mat[i][i]=1;
}
};
Mat A;
Mat operator *(Mat a,Mat b){
Mat ret;
ret.init();
int i,j,k;
for(i=0;i<=sz;i++){
for(j=0;j<=sz;j++)
for(k=0;k<=sz;k++)
ret.mat[i][j]+=a.mat[i][k]*b.mat[k][j];
}
return ret;
}
Mat pow(Mat a,LL p){
Mat ret;
ret.getE();
while(p){
if(p&1) ret=ret*a;
a=a*a;
p>>=1;
}
return ret;
}
int idx(char c){
return c-'a';
}
void Insert(char s[],int v){
int len=strlen(s);
int rt=0;
for(int i=0;i<len;i++){
int t=idx(s[i]);
if(!ch[rt][t]){
memset(ch[sz],0,sizeof(ch[sz]));
ch[rt][t]=sz++;
}
rt=ch[rt][t];
}
val[rt]=v;
}
void getfail(){
queue<int> q;
f[0]=0;
for(int c=0;c<26;c++){
int u=ch[0][c];
if(u){f[u]=0;q.push(u);}
else f[u]=0;
}
while(!q.empty()){
int r=q.front();q.pop();
if(val[f[r]]) val[r]=1;
for(int c=0;c<26;c++){
int u=ch[r][c];
if(!u) {ch[r][c]=ch[f[r]][c];continue;}
q.push(u);
int v=f[r];
while(v&&!ch[v][c]) v=f[v];
f[u]=ch[v][c];
}
}
}
void init(){
memset(ch[0],0,sizeof(ch[0]));
memset(val,0,sizeof(val));
sz=1;
}
int main()
{
//freopen("data.in","r",stdin);
int i,j;
int n;
LL l;
while(scanf("%d %I64d",&n,&l)!=EOF){
init();
for(i=0;i<n;i++){
scanf("%s",str);
Insert(str,1);
}
getfail();
//for(i=0;i<30;i++) printf("%d ",val[i]);printf("\n");
A.init();
for(i=0;i<sz;i++){
if(val[i]) continue;
for(j=0;j<26;j++){
if(val[ch[i][j]]) A.mat[i][sz]++;
else A.mat[i][ch[i][j]]++;
}
}
A.mat[sz][sz]=26;
sz++;A.mat[sz][sz]=A.mat[sz-1][sz]=1;
//A.output();
Mat ret=pow(A,l+1);
printf("%I64u\n",ret.mat[0][sz]);
}
return 0;
}
/*
25 1 0 0  0
24 0 0 0  2
0  0 0 0  0
0  0 0 0  0
0  0 0 0 26*/
/*
25 1 0
24 0 2
0  0 26
*/
【HDU 2825】Wireless Password

看字符串的个数就可以想到应该用状压DP

然后可以先预处理出每一个状态有多少个单词

最后再统计一下单词数>=k的就get了

#include<cstdio>
#include<queue>
#include<cstring>
#include<algorithm>
#define MOD 20090717
using namespace std;
const int SIZEN=205;
int idx(char c){
return c-'a';
}
int ch[SIZEN][26];
int dp[2][SIZEN][1040];
int inc[1040],sz,val[SIZEN];
int f[SIZEN];
char str[200];
void init(){
memset(ch[0],0,sizeof(ch[0]));
memset(val,0,sizeof(val));
memset(dp,0,sizeof(dp));
sz=1;
}
void Insert(char s[],int v){
int len=strlen(s);
int rt=0;
for(int i=0;i<len;i++){
int t=idx(s[i]);
if(!ch[rt][t]){
memset(ch[sz],0,sizeof(ch[sz]));
ch[rt][t]=sz++;
}
rt=ch[rt][t];
}
val[rt]|=(1<<v);
}
void getfail(){
queue<int> q;
f[0]=0;
for(int c=0;c<26;c++){
int u=ch[0][c];
if(u){f[u]=0;q.push(u);}
else f[u]=0;
}
while(!q.empty()){
int r=q.front();q.pop();
val[r]=val[r]|val[f[r]];
for(int c=0;c<26;c++){
int u=ch[r][c];
if(!u) {ch[r][c]=ch[f[r]][c];continue;}
else{
f[u]=ch[f[r]][c];
q.push(u);
}
}
}
}
void getinc(){
for(int i=0;i<(1<<10);i++){
int t=i;
while(t){
inc[i]+=t&1;
t>>=1;
}
}
}
int main()
{
//freopen("data.in","r",stdin);
int i,j,k,l;
int n,m,lim;
int cnt=0;
getinc();
while(scanf("%d%d%d",&n,&m,&lim)!=EOF){
if(!n&&!m&&!lim) break;
init();
cnt=1<<m;
for(i=0;i<m;i++){
scanf("%s",str);
Insert(str,i);
}
getfail();
dp[0][0][0]=1;
for(i=0;i<n;i++){
memset(dp[(i+1)%2],0,sizeof(dp[(i+1)%2]));
for(j=0;j<sz;j++){
for(l=0;l<cnt;l++){
if(dp[i%2][j][l]==0) continue;
for(k=0;k<26;k++){
int nj=ch[j][k];
int nl=val[nj]|l;
int& t_dp=dp[(i+1)%2][nj][nl];
t_dp=(t_dp+dp[i%2][j][l])%MOD;
}
}
}
}
int ans=0;
for(int l=0;l<(1<<m);l++){
if(inc[l]>=lim){
for(j=0;j<sz;j++){
ans=(dp[n%2][j][l]+ans)%MOD;
}
}
}
printf("%d\n",ans);
}
return 0;
}
【HDU 3341】Lost's revenge

(这道题我看了题解我会乱说?_(:з」∠)_)

ORZ之前一直没有想过这样的状态压缩,一直习惯于所有参与的个数都是相同因此同进制

这道题还是给了我很大的启发吧,学习了

其实题目也不会难,首先想到的便是记忆化搜索,但是时间卡得太紧做不到,因此改用递推就可过

重点是状态的保存。状态的压缩目的主要是为了几个参数之间能够不被相互干扰,因此想到用n进制来表示各个参数之间的状态

而这道题很明显用41进制会爆掉,再者A+C+G+T不会超过40,因此就用混合进制(_(:з」∠)_我乱取的名字不要打我)的方法保存就行了

代码略丑_(:з」∠)_,(刚尝试用结构体来写很好玩呀~后面一直都是结构体了)

#include<cstdio>
#include<queue>
#include<cstring>
#include<algorithm>
using namespace std;
const int SIZEN=505;
struct AC{
int ch[SIZEN][4],sz;
int f[SIZEN],val[SIZEN];
char str[SIZEN];
int pow[10],cnt[4];
int dp[SIZEN][16000];
void getpow(){
pow[0]=1;
for(int i=1;i<4;i++)
pow[i]=pow[i-1]*(cnt[i-1]+1);
pow[4]=10000000;
}
void init(){
memset(ch[0],0,sizeof(ch[0]));
memset(val,0,sizeof(val));
memset(cnt,0,sizeof(cnt));
sz=1;
}
int idx(char c){
if(c=='A') return 0;
else if(c=='C') return 1;
else if(c=='G') return 2;
else if(c=='T') return 3;
}
void insert(char s[],int v){
int len=strlen(s);
int rt=0;
for(int i=0;i<len;i++){
int t=idx(s[i]);
if(!ch[rt][t]){
memset(ch[sz],0,sizeof(ch[sz]));
ch[rt][t]=sz++;
}
rt=ch[rt][t];
}
val[rt]+=v;
}
void getfail(){
queue<int> q;
f[0]=0;
for(int c=0;c<4;c++){
int u=ch[0][c];
if(u) q.push(u);f[u]=0;
}
while(!q.empty()){
int r=q.front();q.pop();
if(val[f[r]]) val[r]+=val[f[r]];
for(int c=0;c<4;c++){
int u=ch[r][c];
if(!u) {ch[r][c]=ch[f[r]][c];continue;}
f[u]=ch[f[r]][c];
q.push(u);
}
}
}
int dfs(int u,int s){
if(dp[u][s]!=-1) return dp[u][s];
int ans=0;
for(int i=0;i<4;i++){
if(s%pow[i+1]/pow[i]==0) continue;
ans=max(ans,dfs(ch[u][i],s-pow[i]));
}
ans+=val[u];
return dp[u][s]=ans;
}
int DP(int ss){
dp[0][0]=0;
for(int i=0;i<ss;i++){
for(int j=0;j<sz;j++)
if(dp[j][i]+1){
for(int k=0;k<4;k++){
if((i%pow[k+1]/pow[k]+1)>cnt[k]) continue;
if(i+pow[k]>ss) continue;
int new_j=ch[j][k];
int new_i=i+pow[k];
int &t=dp[new_j][new_i];
t=max(t,dp[j][i]+val[new_j]);
}
}
}
int ans=0;
for(int i=0;i<sz;i++) ans=max(ans,dp[i][ss]);
return ans;
}
int solve(int n){
init();
for(int i=0;i<n;i++){
scanf("%s",str);
insert(str,1);
}
getfail();
scanf("%s",str);
int len=strlen(str);
for(int i=0;i<len;i++) cnt[idx(str[i])]++;
getpow();
int ss=cnt[0]*pow[0]+cnt[1]*pow[1]+cnt[2]*pow[2]+cnt[3]*pow[3];
memset(dp,-1,sizeof(dp[0])*(sz+3));
return DP(ss);
}
};
AC ac;
int main()
{
//freopen("data.in","r",stdin);
int i,j;
int n,txt=1;
while(scanf("%d",&n)!=EOF&&n)
printf("Case %d: %d\n",txt++,ac.solve(n));
}


【HDU 2296】 Ring

这道题就是一个一般的AC自动机+DP的题,难点就在于要输出最短,且字典序最小的题

加一个ans数组保存一下就行了(我调了一下午难道我会乱说?T_T)

在储存节点的值的时候,开始还想着用结构体来保存,但是后来发现根本没必要啊- =。

不需要区分到底是哪一个串,直接把值加上去就行了_(:з」∠)_

#include<cstdio>
#include<queue>
#include<cstring>
#include<algorithm>
using namespace std;
const int SIZEN=1005;
char str[105][20];
int v[105];
struct AC{
int ch[SIZEN][26],sz;
int f[SIZEN];
int val[SIZEN];
int dp_val[55][SIZEN];
char stat[SIZEN];
char ans[55][SIZEN][55];
void init(){
memset(ans,0,sizeof(ans));
memset(dp_val,-1,sizeof(dp_val));
memset(ch[0],0,sizeof(ch[0]));
memset(val,0,sizeof(val));
sz=1;
}
int idx(char c){
return c-'a';
}
void insert(char s[],int id){
int len=strlen(s);
int rt=0;
for(int i=0;i<len;i++){
int t=idx(s[i]);
if(!ch[rt][t]){
memset(ch[sz],0,sizeof(ch[sz]));
stat[sz]=s[i];
ch[rt][t]=sz++;
}
rt=ch[rt][t];
}
val[rt]+=v[id];
}
void getfail(){
queue<int> q;
f[0]=0;
for(int i=0;i<26;i++){
int u=ch[0][i];
if(u) q.push(u);
f[u]=0;
}
while(!q.empty()){
int r=q.front();q.pop();
val[r]=val[r]+val[f[r]];
for(int c=0;c<26;c++){
int u=ch[r][c];
if(!u) {ch[r][c]=ch[f[r]][c];continue;}
f[u]=ch[f[r]][c];
q.push(u);
}
}
}
void solve(int n){
dp_val[0][0]=0;
for(int i=0;i<n;i++){
for(int j=0;j<sz;j++)
if(dp_val[i][j]!=-1){
for(int k=0;k<26;k++){
int ni=i+1;
int nj=ch[j][k];
int t_val=dp_val[i][j]+val[nj];
char t_ans[55];
strcpy(t_ans,ans[i][j]);
t_ans[i]='a'+k;t_ans[i+1]='\0';
if(t_val>dp_val[ni][nj]||(t_val==dp_val[ni][nj]&&strcmp(ans[ni][nj],t_ans)>0)){
dp_val[ni][nj]=t_val;
strcpy(ans[ni][nj],t_ans);
}
}
}
}
int tans=0;
char tstat[55];
for(int i=0;i<=n;i++){
for(int j=0;j<sz;j++){
if(dp_val[i][j]==-1) continue;
if(tans<dp_val[i][j]){
strcpy(tstat,ans[i][j]);
tans=dp_val[i][j];
}
else if(strlen(tstat)<strlen(ans[i][j])) continue;
else if(tans==dp_val[i][j]&&strcmp(tstat,ans[i][j])>0){
tans=dp_val[i][j];
strcpy(tstat,ans[i][j]);
}
}
}
printf("%s\n",tstat);
}
};
AC ac;
int main()
{
//freopen("data.in","r",stdin);
//freopen("data.out","w",stdout);
int i,j;
int n,m,_;
scanf("%d",&_);
while(_--){
scanf("%d%d",&n,&m);
ac.init();
memset(v,0,sizeof(v));
for(i=0;i<m;i++){
scanf("%s",&str[i]);
}
for(i=1;i<=m;i++) scanf("%d",&v[i]);v[0]=0;
for(i=0;i<m;i++) ac.insert(str[i],i+1);
ac.getfail();
ac.solve(n);
}
return 0;
}


【HDU 2457】DNA repair

这道题就是尽量沿着原串在AC自动机上走去走去的,如果和原串不相同就val+1

DP记录一下就可以过了

#include<cstdio>
#include<queue>
#include<cstring>
#include<algorithm>
#define INF 2139062143
using namespace std;
const int SIZEN=1005;
struct AC{
int ch[SIZEN][4],sz;
int f[SIZEN],val[SIZEN];
char ss[SIZEN];
int dp[SIZEN][SIZEN];
void init(){
memset(val,0,sizeof(val));
memset(ch[0],0,sizeof(ch[0]));
memset(dp,127,sizeof(dp));
sz=1;
}
int idx(char c){
if(c=='A') return 0;
if(c=='G') return 1;
if(c=='C') return 2;
if(c=='T') return 3;
}
void insert(char s[]){
int len=strlen(s);
int rt=0;
for(int i=0;i<len;i++){
int t=idx(s[i]);
if(!ch[rt][t]){
memset(ch[sz],0,sizeof(ch[sz]));
ch[rt][t]=sz++;
}
rt=ch[rt][t];
}
val[rt]++;
}
void getfail(){
queue<int> q;
f[0]=0;
for(int c=0;c<4;c++){
int u=ch[0][c];
if(u) q.push(u);f[u]=0;
}
while(!q.empty()){
int r=q.front();q.pop();
if(val[f[r]]) val[r]+=val[f[r]];
for(int c=0;c<4;c++){
int u=ch[r][c];
if(!u) {ch[r][c]=ch[f[r]][c];continue;}
f[u]=ch[f[r]][c];
q.push(u);
}
}
}
int solve(){
int len=strlen(ss);
dp[0][0]=0;
for(int i=0;i<len;i++){
for(int j=0;j<sz;j++){
for(int k=0;k<4;k++){
if(val[ch[j][k]]) continue;
int nj=ch[j][k];
int ni=i+1;
int t_val=dp[i][j];
if(idx(ss[i])!=k) t_val++;
dp[ni][nj]=min(dp[ni][nj],t_val);
}
}
}
int ans=INF;
for(int i=0;i<sz;i++) ans=min(ans,dp[len][i]);
if(ans==INF) return -1;
return ans;
}
};
AC ac;
int main()
{
//freopen("data.in","r",stdin);
int i,j;
int n,txt=1;
char str[SIZEN];
while(scanf("%d",&n)!=EOF&&n){
ac.init();
for(i=0;i<n;i++){
scanf("%s",str);
ac.insert(str);
}
ac.getfail();
scanf("%s",ac.ss);
printf("Case %d: %d\n",txt++,ac.solve());
}
return 0;
}
【HDU 3247】Resource Archiver

专题里面的压轴题了吧,卡了我好几天,最后还是看题解_(:з」∠),果然还是太弱了

因为直接在AC自动机上跑去跑去的会做大量重复的工作,因此直接预处理出串与串之间的dis然后状压DP就OK了

因为才做了西安邀请赛的重现,写了1010才反应过来这个题该怎么来,感人肺腑啊。。。

#include<cstdio>
#include<queue>
#include<cstring>
#include<algorithm>
using namespace std;
const int SIZEN=60005;
const int INF=1000005;
char str[1005];
struct AC{
int ch[SIZEN][2],sz;
int val[SIZEN],f[SIZEN];
int dis[15][15];
int dp[15][1040],len[20];
void init(){
for(int i=0;i<15;i++)
for(int j=0;j<15;j++)
dis[i][j]=i==j?0:INF;
for(int i=0;i<15;i++)
for(int j=0;j<1040;j++) dp[i][j]=INF;
memset(val,0,sizeof(val));
memset(ch[0],0,sizeof(ch[0]));
sz=1;
}
int idx(char c){
return c-'0';
}
int insert(char s[],int v){
int len=strlen(s);
int rt=0;
for(int i=0;i<len;i++){
int t=idx(s[i]);
if(!ch[rt][t]){
memset(ch[sz],0,sizeof(ch[sz]));
ch[rt][t]=sz++;
}
rt=ch[rt][t];
}
val[rt]=v;
return rt;
}
void getfail(){
queue<int> q;
f[0]=0;
for(int i=0;i<2;i++){
int u=ch[0][i];
if(u) q.push(u);
f[u]=0;
}
while(!q.empty()){
int r=q.front();q.pop();
if(val[f[r]]==-1) val[r]=-1;
for(int i=0;i<2;i++){
int u=ch[r][i];
if(!u) {ch[r][i]=ch[f[r]][i];continue;}
f[u]=ch[f[r]][i];
q.push(u);
}
}
}
void query(int s){
queue<int> q;
int dd[SIZEN];
memset(dd,0,sizeof(dd));
q.push(s);
int ss=val[s]-1;
while(!q.empty()){
int u=q.front();q.pop();
for(int i=0;i<2;i++){
int v=ch[u][i];
if(dd[v]||v==s) continue;
if(val[v]==-1) continue;
dd[v]=dd[u]+1;
q.push(v);
if(val[v]) dis[ss][val[v]-1]=dd[v];
}
}
}
int solve(int n){
for(int i=0;i<n;i++) dis[i][i]=0;
for(int i=0;i<n;i++) dp[i][1<<i]=len[i];
for(int i=1;i<(1<<n);i++){
for(int j=0;j<n;j++){
for(int k=0;k<n;k++){
int ni=i|(1<<k);
dp[k][ni]=min(dp[k][ni],dp[j][i]+dis[j][k]);
}
}
}
int ans=INF;
for(int i=0;i<n;i++) ans=min(ans,dp[i][(1<<n)-1]);
return ans;
}
void debug(int n){
for(int i=0;i<n;i++){
for(int j=0;j<n;j++) printf("%7d ",dis[i][j]);
printf("\n");
}
}
};
AC ac;
int pos[20];
int main()
{
int i,j;
int n,m;
while(scanf("%d%d",&n,&m)!=EOF){
if(n==0&&m==0) break;
ac.init();
for(i=0;i<n;i++){
scanf("%s",str);
pos[i]=ac.insert(str,i+1);
ac.len[i]=strlen(str);
}
/*for(i=0;i<ac.sz;i++) printf("%2d ",i);printf("\n");
for(i=0;i<ac.sz;i++) printf("%2d ",ac.val[i]);printf("\n");
for(i=0;i<=n;i++) printf("%d ",pos[i]);printf("\n");*/
for(i=0;i<m;i++){
scanf("%s",str);
ac.insert(str,-1);
}
ac.getfail();
for(i=0;i<n;i++) ac.query(pos[i]);
//ac.debug(n);
printf("%d\n",ac.solve(n));
}
return 0;
}


HDU的题差不多贴完了,吃饭去一会儿再来!

下面是几道其他OJ的题

【ZOJ 3430】Detect the Virus

这道题的难点其实是在于编码的解密吧,然后就是很简单的AC自动机的匹配了

#include<cstdio>
#include<queue>
#include<cstring>
#include<algorithm>
using namespace std;
const int SIZEN=50500;
char str[SIZEN];
int ret[SIZEN];
bool bit[SIZEN];
struct AC{
int ch[SIZEN][256],sz;
int f[SIZEN],last[SIZEN];
int val[SIZEN];
bool flag[1000];
void init(){
memset(ch[0],0,sizeof(ch[0]));
memset(last,0,sizeof(last));
memset(val,0,sizeof(val));
sz=1;
}
void insert(int s[],int v,int len){
int rt=0;
for(int i=0;i<len;i++){
int t=s[i];
if(!ch[rt][t]){
memset(ch[sz],0,sizeof(ch[0]));
ch[rt][t]=sz++;
}
rt=ch[rt][t];
}
val[rt]=v;
}
void getfail(){
queue<int> q;
f[0]=0;
for(int i=0;i<256;i++){
int u=ch[0][i];
if(u) q.push(u);
f[u]=0;
}
while(!q.empty()){
int r=q.front();q.pop();
for(int c=0;c<256;c++){
int u=ch[r][c];
if(!u) {ch[r][c]=ch[f[r]][c];continue;}
f[u]=ch[f[r]][c];
q.push(u);
last[u]=val[f[u]]?f[u]:last[f[u]];
}
}
}
void add(int j){
while(j){
flag[val[j]]=1;
j=last[j];
}
}
void Find(int s[],int len){
int j=0;
for(int i=0;i<len;i++){
int c=s[i];
j=ch[j][c];
if(val[j]) add(j);
else if(last[j]) add(last[j]);
}
}
};
AC ac;
int idx(char c){
if(c<='Z'&&c>='A') return c-'A';
if(c<='z'&&c>='a') return c-'a'+26;
if(c<='9'&&c>='0') return c-'0'+52;
if(c=='+') return 62;
if(c=='-') return 63;
}
int trans(){
int i,j;
int cnt=0;
for(i=0;str[i]!='\0'&&str[i]!='=';i++){
int t=idx(str[i]);
for(j=5;j>=0;j--) bit[cnt++]=t&(1<<j)?1:0;
}
for(;str[i]!='\0';i++) if(str[i]=='=') cnt-=2;
for(i=0;i<cnt/8;i++){
ret[i]=0;
for(j=0;j<8;j++) ret[i]+=bit[i*8+j]<<(7-j);
}
return cnt/8;
}
int main()
{
//freopen("data.in","r",stdin);
int i,j;
int n,m;
while(scanf("%d",&n)!=EOF){
ac.init();
for(i=0;i<n;i++){
scanf("%s",str);
int len=trans();
ac.insert(ret,i+1,len);
}
ac.getfail();
scanf("%d",&m);
for(i=0;i<m;i++){
scanf("%s",str);
memset(ac.flag,0,sizeof(ac.flag));
int len=trans();
ac.Find(ret,len);
int ans=0;
for(j=1;j<=n;j++) ans+=ac.flag[j];
printf("%d\n",ans);
}
printf("\n");
}
return 0;
}
【ZOJ 3228】Searching the String

这道题的主要是要记录该串在上一次遇到的位置,然后就可以确定是否重叠了

#include<cstdio>
#include<queue>
#include<cstring>
#include<algorithm>
using namespace std;
const int SIZEN=100005*6;
char str[SIZEN],ss[20];
struct AC{
int ch[SIZEN][26],sz;
int f[SIZEN],ll[SIZEN];
int cnt[2][SIZEN],val[SIZEN];
int dep[SIZEN],last[SIZEN];
void init(){
memset(last,0,sizeof(last));
memset(val,0,sizeof(val));
memset(ch[0],0,sizeof(ch[0]));
memset(cnt,0,sizeof(cnt));
memset(ll,-1,sizeof(ll));
sz=1;dep[0]=0;
}
int idx(char c){
return c-'a';
}
int insert(char s[]){
int len=strlen(s);
int rt=0;
for(int i=0;i<len;i++){
int t=idx(s[i]);
if(!ch[rt][t]){
memset(ch[sz],0,sizeof(ch[0]));
dep[sz]=dep[rt]+1;
ch[rt][t]=sz++;
}
rt=ch[rt][t];
}
val[rt]=1;
return rt;
}
void getfail(){
queue<int> q;
f[0]=0;
for(int i=0;i<26;i++){
int u=ch[0][i];
if(u) q.push(u);
f[u]=0;
}
while(!q.empty()){
int r=q.front();q.pop();
for(int i=0;i<26;i++){
int u=ch[r][i];
if(!u) {ch[r][i]=ch[f[r]][i];continue;}
f[u]=ch[f[r]][i];
q.push(u);
last[u]=val[f[u]]?f[u]:last[f[u]];
}
}
}
void add(int j,int loc){
while(j){
cnt[0][j]++;
if(loc-ll[j]>=dep[j]){
cnt[1][j]++;
ll[j]=loc;
}
j=last[j];
}
}
void find(char s[]){
int len=strlen(s);
int j=0;
for(int i=0;i<len;i++){
int c=idx(s[i]);
j=ch[j][c];
add(j,i);
}
}
};
AC ac;
int pos[SIZEN],type[SIZEN];
int txt=1;
int main()
{
//freopen("data.in","r",stdin);
int i,j;
int n;
while(scanf("%s",str)!=EOF){
scanf("%d",&n);
ac.init();
for(i=0;i<n;i++){
scanf("%d %s",&type[i],ss);
pos[i]=ac.insert(ss);
}
ac.getfail();
ac.find(str);
printf("Case %d\n",txt++);
for(i=0;i<n;i++) printf("%d\n",ac.cnt[type[i]][pos[i]]);
printf("\n");
}
return 0;
}


【POJ 2778】DNA Sequence

这道题是很基础的在AC自动机上面跑DP的题了

也是需要用到矩阵,是HDU 2243的简化版吧

#include<cstdio>
#include<queue>
#include<cstring>
#include<algorithm>
#define mod 100000
using namespace std;
typedef long long LL;
const int SIZEN=110;
char str[100];
struct Mat{
LL mat[SIZEN][SIZEN];
void init(){
memset(mat,0,sizeof(mat));
}
void getE(){
memset(mat,0,sizeof(mat));
for(int i=0;i<SIZEN;i++) mat[i][i]=1;
}
void output(int sz){
for(int i=0;i<sz;i++){
for(int j=0;j<sz;j++) printf("%I64d ",mat[i][j]);
printf("\n");
}
}
};
struct AC{
int ch[SIZEN][4],sz;
int f[SIZEN],val[SIZEN];
int idx(char c){
if(c=='A') return 0;
if(c=='G') return 1;
if(c=='C') return 2;
if(c=='T') return 3;
}
void init(){
memset(ch[0],0,sizeof(ch[0]));
memset(val,0,sizeof(val));
sz=1;
}
void insert(char s[]){
int len=strlen(s);
int rt=0;
for(int i=0;i<len;i++){
int t=idx(str[i]);
if(!ch[rt][t]){
memset(ch[sz],0,sizeof(ch[0]));
ch[rt][t]=sz++;
}
rt=ch[rt][t];
}
val[rt]=1;
}
void getfail(){
queue<int> q;
f[0]=0;
for(int i=0;i<4;i++){
int u=ch[0][i];
if(u) {q.push(u);f[u]=0;}
}
while(!q.empty()){
int r=q.front();q.pop();
if(val[f[r]]) val[r]=1;
for(int i=0;i<4;i++){
int u=ch[r][i];
if(!u) {ch[r][i]=ch[f[r]][i];continue;}
f[u]=ch[f[r]][i];
q.push(u);
}
}
}
};
AC ac;
Mat A;
Mat operator *(Mat a,Mat b){
Mat ret;
ret.init();
for(int i=0;i<=ac.sz;i++)
for(int j=0;j<=ac.sz;j++)
for(int k=0;k<=ac.sz;k++){
ret.mat[i][j]=(ret.mat[i][j]+a.mat[i][k]*b.mat[k][j])%mod;
}
return ret;
}
Mat operator ^(Mat a,int p){
Mat ret;
ret.getE();
while(p){
if(p&1) ret=ret*a;
a=a*a;
p>>=1;
}
return ret;
}
LL pow(int b){
LL ret=1,a=4;
while(b){
if(b&1) ret=ret*a%mod;
a=a*a%mod;
b>>=1;
}
return ret;
}
int main()
{
int i,j;
int n,m;
while(scanf("%d%d",&m,&n)!=EOF){
ac.init();
for(i=0;i<m;i++){
scanf("%s",str);
ac.insert(str);
}
ac.getfail();
A.init();
for(i=0;i<ac.sz;i++){
if(ac.val[i]) continue;
for(j=0;j<4;j++){
int nj=ac.ch[i][j];
if(ac.val[nj]) A.mat[i][ac.sz]++;
else A.mat[i][nj]++;
}
}
A.mat[ac.sz][ac.sz]=4;
//A.output(ac.sz+2);
Mat ret=A^n;
//ret.output(ac.sz+2);
LL ans=(pow(n)-ret.mat[0][ac.sz]+mod)%mod;
printf("%I64d\n",ans);
}
return 0;
}


【POJ 1625】Censored!

这道题在想法上没什么多说的,主要是练习了一下高精度的使用(竟然被高精度坑了有1个多小时_(:з」∠)_)

#include<cstdio>
#include<queue>
#include<cstring>
#include<algorithm>
#define mod 10000000
using namespace std;
const int SIZEN=155;
char str[SIZEN];
struct Big{
int num[205];
void init(){
memset(num,0,sizeof(num));
}
void output(){
int j=204;
while(!num[j]&&j>0) j--;
printf("%d",num[j--]);
while(j>=0) printf("%07d",num[j--]);
printf("\n");
}
};
Big operator + (Big a,Big b){
Big ret;
ret.init();
for(int i=0;i<SIZEN;i++)
ret.num[i]=a.num[i]+b.num[i];
for(int i=0;i<SIZEN-1;i++){
ret.num[i+1]+=ret.num[i]/mod;
ret.num[i]%=mod;
}
return ret;
}
struct AC{
char ss[SIZEN];
int ch[105][51],sz;
int f[105],val[105];
int map[515];
Big dp[51][105];
void init(){
memset(map,-1,sizeof(map));
memset(val,0,sizeof(val));
memset(ch[0],0,sizeof(ch[0]));
for(int i=0;i<51;i++)
for(int j=0;j<105;j++) dp[i][j].init();
sz=1;
}
void getmap(){
gets(ss);
int len=strlen(ss);
for(int i=0;i<len;i++)
map[ss[i]+128]=i;
}
int idx(char c){
return map[c+128];
}
void insert(char s[],int v){
int len=strlen(s);
int rt=0;
for(int i=0;i<len;i++){
int t=idx(s[i]);
if(!ch[rt][t]){
memset(ch[sz],0,sizeof(ch[0]));
ch[rt][t]=sz++;
}
rt=ch[rt][t];
}
val[rt]=v;
}
void getfail(int n){
queue<int> q;
f[0]=0;
for(int i=0;i<n;i++){
int u=ch[0][i];
if(u) q.push(u);f[u]=0;
}
while(!q.empty()){
int r=q.front();q.pop();
if(val[f[r]]) val[r]=val[f[r]];
for(int i=0;i<n;i++){
int u=ch[r][i];
if(!u) {ch[r][i]=ch[f[r]][i];continue;}
f[u]=ch[f[r]][i];
q.push(u);
}
}
}
void solve(int n,int m){
dp[0][0].num[0]=1;
for(int i=0;i<m;i++){
int ni=i+1;
for(int j=0;j<sz;j++)
if(val[j]==0){
for(int k=0;k<n;k++){
int nj=ch[j][k];
if(val[nj]) continue;
dp[ni][nj]=dp[ni][nj]+dp[i][j];
}
}
}
}
};
AC ac;
int main()
{
//freopen("data.in","r",stdin);
//freopen("data.out","w",stdout);
int i,j;
int n,m,p;
while(scanf("%d%d%d",&n,&m,&p)!=EOF){
ac.init();
getchar();
ac.getmap();
for(i=0;i<p;i++){
gets(str);
ac.insert(str,1);
}
ac.getfail(n);
ac.solve(n,m);
Big ans;
ans.init();
for(i=0;i<ac.sz;i++) ans=ans+ac.dp[m][i];
ans.output();
}
return 0;
}


好了差不多就只能刷这么多题了T_T
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: