UVALive 4126 (ac自动机做状态类)
2015-10-09 15:40
295 查看
题意:
给定最多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
,0,sizeof(d
));
for(int j=0;j<maxnnode;j++) d
[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;
}
给定最多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
,0,sizeof(d
));
for(int j=0;j<maxnnode;j++) d
[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;
}
相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- 关于指针的一些事情
- c++ primer 第五版 笔记前言
- share_ptr的几个注意点
- Lua中调用C++函数示例
- Lua教程(一):在C++中嵌入Lua脚本
- Lua教程(二):C++和Lua相互传递数据示例
- C++联合体转换成C#结构的实现方法
- C++编写简单的打靶游戏
- C++ 自定义控件的移植问题
- C++变位词问题分析
- C/C++数据对齐详细解析
- C++基于栈实现铁轨问题
- C++中引用的使用总结
- 使用Lua来扩展C++程序的方法
- C++中调用Lua函数实例
- Lua和C++的通信流程代码实例
- C与C++之间相互调用实例方法讲解
- 解析C++中派生的概念以及派生类成员的访问属性
- C++ Custom Control控件向父窗体发送对应的消息