UVALive 4126 (ac自动机做状态类)

题意:

给定最多10个长度不超过10的子串,让构造长度为n(n<=25)的字符串使得给定的子串都在该串中出现,问这种串有多少。

分析:

暴力是26^25中可能性,那么怎样精简状态,可以定义d[ i ][ j ][ s ]为当前构造的串长为i且已经到达自动机j位置,已经生成的子串状态为s,接着往下构造所能生成的合法串有多少。

状态转移,就是直接暴力枚举一下下一个字母是谁,在自动机中做状态转移。

一开始用ac自动机做状态不是很好理解,j其实是代表着前面走到i位置,所有最大匹配在自动机中为j的串,那么转移也如此。

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <iostream>
#include <queue>
#include <set>
#include <string>
using namespace std;
typedef long long ll;
#define rep(i,n) for(int i=0;i<(int)n;i++)
#define rep1(i,x,y) for(int i=x;i<=y;i++)

const int maxnnode = 110;
const int sigma_size = 26;
struct Trie{
  int ch[maxnnode][sigma_size];
  int val[maxnnode],f[maxnnode],last[maxnnode];
  int cnt;
  void init(){
     cnt = 0; memset(ch[0],0,sizeof(ch[0]));
     val[0] = 0;
     memset(val,0,sizeof(val));
  }
  int id(char c){return c-'a';}
  void insert(char* s,int x){
     int n=strlen(s),u=0;
     rep(i,n){
        int c = id(s[i]);
        if(!ch[u][c]){
            ch[u][c]=++cnt;
            val[cnt]=0;
            memset(ch[cnt],0,sizeof(ch[cnt]));
        }
        u = ch[u][c];
     }
     val[u] = x;
  }
  void build(){
      f[0] = last[0]=0;
      queue<int> Q;
      rep(i,sigma_size){
          if(ch[0][i]) Q.push(ch[0][i]),f[ch[0][i]]=last[ch[0][i]]=0;
      }
      while(!Q.empty()){
          int u=Q.front(); Q.pop();
          rep(i,sigma_size){
               if(!ch[u][i]){
                   ch[u][i] = ch[f[u]][i];
                   continue;
               }
               int v = ch[u][i];
               f[v] = ch[f[u]][i];
               last[v] = (val[f[v]] ? f[v] : last[f[v]]);
               Q.push(v);
          }
      }
  }
  int print_(int u){
      cnt = 0;
      while(u) {
          cnt|=(1<<(val[u]-1));
          u = last[u];
      }
      return cnt;
  }
}ac;
const int maxn = 26;
ll d[maxn][maxnnode][(1<<10)+10];
int n,m;
ll dp(){
   memset(d[n],0,sizeof(d[n]));
   for(int j=0;j<maxnnode;j++) d[n][j][(1<<m)-1]=1;
   int te = ac.cnt;
   for(int i=n-1;i>=0;i--)
      rep1(j,0,te)
        for(int s=0;s<(1<<m);s++){
          d[i][j][s] = 0;
          rep(k,sigma_size){
            int c = ac.ch[j][k];
            int ts = ac.print_(ac.val[c] ? c : ac.last[c]);
            d[i][j][s]+=d[i+1][c][(s|ts)];
          }
   }
   return d[0][0][0];
}
vector<char> aa;
void print_(int i,int j,int s){
   if(i == n){
       rep(i,aa.size()) cout<<aa[i]; cout<<endl;
       return ;
   }
   rep(k,sigma_size){
       int c = ac.ch[j][k];
       int ts = ac.print_(ac.val[c] ? c : ac.last[c]);
       if(d[i+1][c][s|ts]) {
           aa.push_back('a'+k);
           print_(i+1,c,s|ts);
           aa.pop_back();
       }
   }
}
set<string> vvis;
int main()
{
   int kase=1;
   vvis.clear();
   while(scanf("%d %d",&n,&m)==2 && n){
       vvis.clear();
       ac.init();
       int rm = 0;
       rep(i,m){
          char s[30];
          scanf("%s",s);
          string te = s ;
          if(!vvis.count(te)){
             ac.insert(s,++rm);
             vvis.insert(te);
          }
       }
       m = rm;
       ac.build();
       ll ans = dp();
       aa.clear();
       printf("Case %d: %lld suspects\n",kase++,ans);
       if(ans <= 42){
           print_(0,0,0);
       }
   }
   return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值