hdu3247自动机+TSP
题目:http://acm.hdu.edu.cn/showproblem.php?pid=3247
题目大意:给定n个串(n <= 10,length <= 1000) 和 m个病毒串(总字符数50000),求一个新串,包含n个串全部但不能包含m中的任意一个,串可重叠出现。输出最短长度。
思路:建立一个自动机是肯定的,不能出现的标记-1。一开始想了一个错误的算法,类似SPFA,定义dp[i]用位记录包含n中的那几个串。num[i]表示在dp[i]出现的状态下最小长度。。。。多么傻的想法啊,直接就wa了。自动机上的状态后效性全无,怎么这样递推呢。。。。。。于是只能换思路了。 。。。。。思考中。。。。。思考中。。。。天色已晚,只能明天想了。!!!!睡觉中。。梦到人工模拟状态转移掉入死循环突然惊醒。。。看看表5:40。。。继续睡。8点起床继续想。。。感觉我们要的是串与串间的合并, 似乎和状态机中的末尾节点关系很大,而和中间节点没什么关系。。。怎么利用这个现象呢。。。如果从某个危险节点(即某个串的末尾在自动机中的位置)出发宽搜一次,那么我们就能确定把其它串接到这个串后边且不出现病毒串的最小长度,这样利用O(10*50000)就能确定任意两个串之间的最短距离。。。。。然后只有10个串, 于是TSP可以发威了。。。喔喔,可以搞了。。咦?子串怎么办呢?。。。想想想。。没有好方法,直接提前判一下,然后将n中所有是别人子串的去掉。又过了若干小时。。哇! 一次AC啊。菜鸟内牛满面啊~‘~
#include <cstdio>
#include <cstring>
#define MAXN 11
#define MAXL 1001
#define MAXSTATE 70100
#define SIZE 2
#define INF 21000000
#define MAXG 10000
struct _Trie{
int next[SIZE];
int id;
int suffix;
};
struct _String{
char st[MAXL];
int len;
};
_String need[MAXN]; //n个串
_Trie trie[MAXSTATE];
int dis[MAXN][MAXN], state, n; //dis记录两个串间的最短连接代价
int pos[MAXN]; //记录串i在自动机中的位置。
char st[MAXSTATE];
int queue[MAXSTATE];
int f[MAXN][MAXG];//TSP记录从v出发走完图G的最小花费
void clear(){
state = 1;
trie[0].id = 0;
for(int i = 0; i < SIZE; ++ i) trie[0].next[i] = -1;
}
void insert(char *str, int id){
int rt = 0;
for(int i = 0; str[i] != '\0'; ++ i){
if(trie[rt].next[str[i] - '0'] == -1){
trie[rt].next[str[i] - '0'] = state;
trie[state].id = 0;
trie[state].next[0] = trie[state].next[1] = -1;
rt = state ++;
}
else rt = trie[rt].next[str[i] - '0'];
}
if(id == -1) trie[rt].id = -1;
else{
trie[rt].id = id + 1;
pos[id] = rt;
}
}
int find(int u, int v){
for(int i = 0, j; i <= need[v].len - need[u].len; ++ i){
for(j = 0; j < need[u].len; ++ j){
if(need[u].st[j] != need[v].st[i + j]) break;
}
if(j == need[u].len) return 1;
}
return 0;
}//判断u是不是v的子串
void slove(){
for(int i = 0; i < n; ++ i){
scanf("%s", need[i].st);
need[i].len = strlen(need[i].st);
}
for(int i = 0; i < n; ++ i){
if(need[i].len == 0) continue;
for(int j = 0; j < n; ++ j){
if(need[j].len == 0 || i == j || need[i].len > need[j].len) continue;
if(find(i, j)){
need[i].len = 0;
}
}
}
for(int i = 0; i < n;){
if(need[i].len == 0){
n --;
for(int j = i; j < n ; ++ j)
need[j] = need[j + 1];
}
}
for(int i = 0; i < n; ++ i){
insert(need[i].st, i);
}
}//处理个串中子串问题
void input(int m){
for(int i = 0; i < m; ++ i){
scanf("%s", st);
insert(st, -1);
}
}//将病毒串插入trie树
void built_trie_map(){
int head, tail, father, child;
head = tail = trie[0].suffix = 0;
queue[tail ++] = 0;
while(head < tail){
father = queue[head ++];
for(int i = 0; i < SIZE; ++ i){
child = trie[father].next[i];
if(child == -1) trie[father].next[i] = trie[trie[father].suffix].next[i] * (father != 0);
else{
trie[child].suffix = trie[trie[father].suffix].next[i] * (father != 0);
if(trie[trie[child].suffix].id == -1) trie[child].id = -1;
queue[tail ++] = child;
}
}
}
}//建立自动机
int step[MAXSTATE];
void get_dis(int u){
int head, tail, opt, ci;
head = tail = 0;
memset(step, 0, sizeof(step));
step[pos[u]] = 1;
queue[tail ++] = pos[u];
while(head < tail){
opt = queue[head ++];
for(int i = 0; i < SIZE; ++ i){
ci = trie[opt].next[i];
if(trie[ci].id == -1 || step[ci]) continue;
step[ci] = step[opt] + 1;
queue[tail ++] = ci;
if(trie[ci].id > 0){
dis[u][trie[ci].id - 1] = step[ci] - 1;
}
}
}
}//利用宽搜求u连接其它串的最小代价。
int dfs(int v, int G){
if(!G) return (f[v][G] = dis[v][n]);
if(f[v][G] != -1) return f[v][G];
int i, min = INF, temp;
for(i = 0; i < n; ++ i){
if(G & (1 << i)){
G ^= (1 << i);
temp = dfs(i, G);
G ^= (1 << i);
if(min > dis[v][i] + temp) min = dis[v][i] + temp;
}
}
return (f[v][G] = min);
}//TSP记忆话状态压缩搜索。
void get_ans(){
int i, j, G;
for(i = 0; i < n; ++ i){
dis[n][i] = need[i].len;
dis[i][n] = 0;
}
memset(f, -1, sizeof(f));
G = (1 << n) - 1;
dfs(n, G);
printf("%d\n", f[n][G]);
}//TSP
int main(){
int m;
while(~scanf("%d %d", &n, &m), n){
clear();
slove();
input(m);
built_trie_map();
for(int i = 0; i < n; ++ i){
for(int j = 0; j < n; ++ j){
dis[i][j] = INF;
}
}
for(int i = 0; i < n; ++ i){
get_dis(i);
}
get_ans();
}
return 0;
}