Luogu 2322 [HNOI2006]最短母串问题
唔,太菜了,弄了好几个小时。
状压dp,设$f_{s, i}$表示选了集合$s$,以$i$结尾的最短长度,设$g_{i, j}$表示串$i$的后缀和串$j$的前缀的最长匹配长度。
$f_{s, i} + len_{j} - g_{i, j} $ 可以转移到$f_{s | (1 << (j - 1)), j}$ $(i\in s, j\notin s)$。
转移的时候发现两个串的长度一样要把这两个的答案都弄出来比一比字典序。
如果一个串是另一个串的字串,不参与转移。
注意特判全部串相同的情况。
可以用kmp优化$g$的计算和判断子串,但是$n$太小了,所以时间并不会差太多。
时间复杂度$O(能过)$。
用AC自动机 + BFS转移会更优美.
Code:
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int N = 15; const int S = (1 << 15) + 5; const int L = 55; const int inf = 0x3f3f3f3f; int n, nowLen, len[N], g[N][N], f[S][N], pre[S][N]; bool mark[N]; struct MyStr { char s[L]; } a[N]; inline void chkMin(int &x, int y) { if(y < x) x = y; } inline bool chk(int x, int y, int l) { for(int i = len[x] - l + 1, j = 1; j <= l; i++, j++) if(a[x].s[i] != a[y].s[j]) return 0; return 1; } inline bool con(char *s1, int y) { char *s2 = a[y].s; if(len[y] > nowLen) return 0; for(int i = 1; i <= nowLen; i++) if(s1[i] == s2[1]) { bool flag = 1; for(int k = i + 1, j = 2; j <= len[y]; j++, k++) if(s2[j] != s1[k]) { flag = 0; break; } if(flag) return 1; } return 0; } inline void getS(int nowS, int now, char *str) { if(!pre[nowS][now]) { nowLen = 0; for(int i = 1; i <= len[now]; i++) str[++nowLen] = a[now].s[i]; return; } if(pre[nowS][now]) getS(nowS ^ (1 << (now - 1)), pre[nowS][now], str); // if(con(str, now)) return; for(int i = g[pre[nowS][now]][now] + 1; i <= len[now]; i++) str[++nowLen] = a[now].s[i]; } inline bool myCmp(char *s1, char *s2, int l) { for(int i = 1; i <= l; i++) if(s1[i] < s2[i]) return 1; else if(s1[i] > s2[i]) return 0; return 0; } int main() { scanf("%d", &n); for(int i = 1; i <= n; i++) scanf("%s", a[i].s + 1); // sort(a + 1, a + 1 + n, cmp); for(int i = 1; i <= n; i++) len[i] = strlen(a[i].s + 1); for(int i = 1; i <= n; i++) for(int j = 1; j <= n; j++) { if(i == j) { g[i][j] = len[i]; continue; } for(int k = min(len[i], len[j]); k >= 1; k--) if(chk(i, j, k)) { g[i][j] = k; break; } } for(int i = 1; i <= n; i++) { nowLen = len[i]; for(int j = 1; j <= n; j++) { if(j == i) continue; if(con(a[i].s, j)) mark[j] = 1; } } bool flag = 0; for(int i = 1; i <= n; i++) if(!mark[i]) { flag = 1; break; } if(!flag) { puts(a[1].s + 1); return 0; } /* printf("\n"); for(int i = 1; i <= n; i++) printf("%s\n", a[i].s + 1); printf("\n"); for(int i = 1; i <= n; i++, printf("\n")) for(int j = 1; j <= n; j++) printf("%d ", g[i][j]); */ memset(f, 0x3f, sizeof(f)); for(int i = 1; i <= n; i++) if(!mark[i]) f[1 << (i - 1)][i] = len[i]; char str1[N * L], str2[N * L]; for(int s = 1; s < (1 << n); s++) for(int i = 1; i <= n; i++) if((s & (1 << (i - 1))) && f[s][i] != inf && !mark[i]) for(int j = 1; j <= n; j++) if(!(s & (1 << (j - 1))) && !mark[j]) { // getS(s, i, str1); int now = len[j] - g[i][j]; if(f[s][i] + now < f[s | (1 << (j - 1))][j]) { f[s| (1 << (j - 1))][j] = now + f[s][i]; pre[s| (1 << (j - 1))][j] = i; } else if(f[s][i] + now == f[s | (1 << (j - 1))][j]) { getS(s | (1 << (j - 1)), j, str1), getS(s, i, str2); for(int k = g[i][j] + 1; k <= len[j]; k++) str2[++nowLen] = a[j].s[k]; if(myCmp(str2, str1, nowLen)) pre[s | (1 << (j - 1))][j] = i; } } int ans = inf, curS = (1 << n) - 1; for(int i = 1; i <= n; i++) if(mark[i]) curS ^= (1 << (i - 1)); for(int i = 1; i <= n; i++) chkMin(ans, f[curS][i]); int pos = 0; for(int i = 1; i <= n; i++) if(f[curS][i] == ans) { getS(curS, i, str1); pos = i; break; } for(int i = pos + 1; i <= n; i++) { if(f[curS][i] > ans) continue; getS(curS, i, str2); /* for(int j = 1; j <= ans; j++) putchar(str1[j]); printf("\n"); for(int j = 1; j <= ans; j++) putchar(str2[j]); printf("\n"); */ if(myCmp(str2, str1, ans)) for(int j = 1; j <= ans; j++) str1[j] = str2[j]; } for(int i = 1; i <= ans; i++) putchar(str1[i]); printf("\n"); return 0; }