题意:给n个疾病的DNA序列和一个要修复的DNA序列,求最少换掉多少个字母,使得DNA序列不含疾病。不能修复输出-1.
dp[i][j]表示长度为以j节点结尾的串与给定串差异的最小值。
转移的时候,不能走病毒串的尾节点。
具体看代码。
//#pragma comment(linker,"/STACK:1024000000,1024000000")
#include <map>
#include <set>
#include <cmath>
#include <queue>
#include <stack>
#include <cstdio>
#include <string>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> PII;
#define pb push_back
#define MP make_pair
#define lson l, m, rt << 1
#define rson m + 1, r, rt << 1 | 1
const double eps = 1e-6;
const int inf = 0x3f3f3f3f;
const int mod = 100000;
const int maxn = 1100 + 10;
const int max_chd = 4;///字母表大小
struct AC_automata{
int chd[maxn][max_chd], fail[maxn], cnt[maxn];///trie中的chd数组,fail指针,节点标记(是多少个串的结尾)
int root, sz;///根节点,trie树大小
int newnode(){///建立新的节点,返回节点编号
for(int i=0; i<max_chd; i++) chd[sz][i] = -1;
cnt[sz++] = 0;
return sz - 1;
}
void init(){
sz = 0;
root = newnode();
}
int id(char ch){
if(ch == 'A') return 0;
if(ch == 'T') return 1;
if(ch == 'G') return 2;
if(ch == 'C') return 3;
}
void Insert(char * str){///建trie树
int cur = root;
for(int i=0; str[i]; i++){
if(chd[cur][id(str[i])] == -1)
chd[cur][id(str[i])] = newnode();
cur = chd[cur][id(str[i])];
}
cnt[cur] = 1;
}
void build(){///构建fail指针
queue<int> Q;
while(!Q.empty()) Q.pop();
fail[root] = root;
for(int i=0; i<max_chd; i++){
if(chd[root][i] == -1)
chd[root][i] = root;
else{
fail[chd[root][i]] = root;
Q.push(chd[root][i]);
}
}
while(!Q.empty()){
int now = Q.front(); Q.pop();
if(cnt[fail[now]] == 1) cnt[now] = 1;
for(int i=0; i<max_chd; i++){
if(chd[now][i] == -1)
chd[now][i] = chd[fail[now]][i];
else {
fail[chd[now][i]] = chd[fail[now]][i];
Q.push(chd[now][i]);
}
}
}
}
int dp[maxn][maxn];
int Query(char * str){
memset(dp, 0x3f, sizeof(dp));
dp[0][root] = 0;
for(int i=0; str[i]; i++){
for(int j=0; j<sz; j++)if(dp[i][j] < inf){
for(int k=0; k<max_chd; k++){
int now = chd[j][k];
if(cnt[now]) continue; ///不能走过尾节点
int tmp;
if(id(str[i]) == k) tmp = dp[i][j];
else tmp = dp[i][j] + 1;<span style="font-family: Arial, Helvetica, sans-serif;">///当当前节点与给定串不同时,不同之数加1,否则不变</span>
dp[i + 1][now] = min(dp[i + 1][now], tmp);///其实就是在构建不含尾节点的长度为n的串,求与原串的不同之处最少
}
}
}
int len = strlen(str);
int ans = inf;
for(int i=0; i<sz; i++)
ans = min(ans, dp[len][i]);
if(ans == inf) return -1;
else return ans;
}
}AC;
int m, n, cas = 0;
char str[1050];
int main(){
while(scanf("%d", &n) == 1 && n){
AC.init();
for(int i=1; i<=n; i++){
scanf("%s", str);
AC.Insert(str);
}
AC.build();
scanf("%s", str);
printf("Case %d: %d\n", ++cas, AC.Query(str));
}
return 0;
}
题意:给m个字符串,每个字符串有一定的价值,求一个长度不超过n 的字符串,使得它的价值最大。
dp[i][j]表示长度不超过i,以节点j结尾的字符串的最大价值。在用一个字符串数组维护这个字符串即可。
//#pragma comment(linker,"/STACK:1024000000,1024000000")
#include <map>
#include <set>
#include <cmath>
#include <queue>
#include <stack>
#include <cstdio>
#include <string>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> PII;
#define pb push_back
#define MP make_pair
#define lson l, m, rt << 1
#define rson m + 1, r, rt << 1 | 1
const double eps = 1e-6;
const int inf = 0x3f3f3f3f;
const int mod = 100000;
const int maxn = 1100 + 10;
const int max_chd = 26;///字母表大小
struct AC_automata{
int chd[maxn][max_chd], fail[maxn], cnt[maxn];///trie中的chd数组,fail指针,节点标记(是多少个串的结尾)
int root, sz;///根节点,trie树大小
int newnode(){///建立新的节点,返回节点编号
for(int i=0; i<max_chd; i++) chd[sz][i] = -1;
cnt[sz++] = 0;
return sz - 1;
}
void init(){
sz = 0;
root = newnode();
}
int id(char ch){
return ch - 'a';
}
void Insert(char * str, int val){///建trie树
int cur = root;
for(int i=0; str[i]; i++){
if(chd[cur][id(str[i])] == -1)
chd[cur][id(str[i])] = newnode();
cur = chd[cur][id(str[i])];
}
cnt[cur] = val;
}
void build(){///构建fail指针
queue<int> Q;
while(!Q.empty()) Q.pop();
fail[root] = root;
for(int i=0; i<max_chd; i++){
if(chd[root][i] == -1)
chd[root][i] = root;
else{
fail[chd[root][i]] = root;
Q.push(chd[root][i]);
}
}
while(!Q.empty()){
int now = Q.front(); Q.pop();
for(int i=0; i<max_chd; i++){
if(chd[now][i] == -1)
chd[now][i] = chd[fail[now]][i];
else {
fail[chd[now][i]] = chd[fail[now]][i];
Q.push(chd[now][i]);
}
}
}
}
bool ok(char * A, char * B){
int a = strlen(A);
int b = strlen(B);
return (a < b) || (a == b && strcmp(A, B) < 0);
}
int dp[maxn][maxn];
char ss[maxn][maxn][55];
void solve(int n){
for(int i=0; i<=n; i++) for(int j=0; j<=sz; j++) dp[i][j] = -inf;
int mx = 0;
dp[0][0] = 0;
ss[0][0][0] = '\0';
char ans[55], tmp[55];
ans[0] = '\0';
for(int i=0; i<n; i++){
for(int j=0; j<sz; j++)if(dp[i][j] >= 0){
strcpy(tmp, ss[i][j]);
int len = strlen(tmp);
for(int k=0; k<max_chd; k++){
int now = chd[j][k];
tmp[len] = k + 'a';
tmp[len + 1] = '\0';
int kk = dp[i][j];
if(cnt[now]) kk += cnt[now];
if(dp[i + 1][now] < kk || (dp[i + 1][now] == kk && ok(tmp, ss[i + 1][now]))){
dp[i + 1][now] = kk;
strcpy(ss[i + 1][now], tmp);
if(kk > mx || (kk == mx && ok(tmp, ans))){
mx = kk;
strcpy(ans, tmp);
}
}
}
}
}
printf("%s\n", ans);
}
}AC;
int m, n, T, v;
char str[111][20];
int main(){
scanf("%d", &T);
while(T--){
scanf("%d%d", &n, &m);
AC.init();
for(int i=1; i<=m; i++)
scanf("%s", str[i]);
for(int i=1; i<=m; i++){
scanf("%d", &v);
AC.Insert(str[i], v);
}
AC.build();
AC.solve(n);
}
return 0;
}
HDU -- 2825 Wireless Password
题意:给m个字符串,要求长度为n的至少包含k个给定字符串的个数。
dp[i][j][k]表示长度为i,以j节点结尾,包含给定字符串的状态为k的个数。(这题非常容易T。。。QAQ
#include <map>
#include <set>
#include <cmath>
#include <queue>
#include <stack>
#include <cstdio>
#include <string>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef double DB;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> PII;
#define pb push_back
#define MP make_pair
#define lson l, m, rt << 1
#define rson m + 1, r, rt << 1 | 1
const DB eps = 1e-6;
const int inf = 0x3f3f3f3f;
const int mod = 20090717;
const int maxn = 100 + 10;
int n, m, k, dp[30][maxn][1 << 11];
char str[maxn];
int judge[1 << 11];
const int max_chd = 26;
struct AC_automata{
int chd[maxn][max_chd], fail[maxn], cnt[maxn];
int root, sz;
int newnode(){
for(int i=0; i<max_chd; i++)
chd[sz][i] = -1;
cnt[sz++] = 0;
return sz - 1;
}
void init(){
sz = 0;
root = newnode();
}
int ID(char ch){
return ch - 'a';
}
void Insert(char * str, int id){
int cur = root;
for(int i=0; str[i]; i++){
if(chd[cur][ID(str[i])] == -1)
chd[cur][ID(str[i])] = newnode();
cur = chd[cur][ID(str[i])];
}
cnt[cur] |= (1 << id);
}
void build(){
queue<int> Q;
while(!Q.empty()) Q.pop();
fail[root] = root;
for(int i=0; i<max_chd; i++){
if(chd[root][i] == -1)
chd[root][i] = root;
else {
fail[chd[root][i]] = root;
Q.push(chd[root][i]);
}
}
while(!Q.empty()){
int now = Q.front(); Q.pop();
cnt[now] |= cnt[fail[now]];
for(int i=0; i<max_chd; i++){
if(chd[now][i] == -1)
chd[now][i] = chd[fail[now]][i];
else{
fail[chd[now][i]] = chd[fail[now]][i];
Q.push(chd[now][i]);
}
}
}
}
void solve(){
for(int i=0; i<=n; i++) for(int j=0; j<=sz; j++) for(int k=0; k<=(1<<m); k++) dp[i][j][k] = 0;
dp[0][0][0] = 1;
for(int i=0; i<n; i++){
for(int j=0; j<sz; j++){
for(int k=0; k<(1<<m); k++) if(dp[i][j][k] > 0){
for(int ch=0; ch<max_chd; ch++){
int now = chd[j][ch];
int tmp = (k | cnt[now]);
dp[i + 1][now][tmp] += dp[i][j][k];
dp[i + 1][now][tmp] %= mod;
}
}
}
}
// cout << dp[n][5][3] <<" " << dp[n][10][3] << endl;
int ans = 0;
for(int i=0; i<(1<<m); i++)
if(judge[i] >= k){
for(int j=0; j<sz; j++){
ans += dp[n][j][i];
ans %= mod;
}
}
printf("%d\n", ans);
}
}AC;
int main(){
for(int i=0; i<(1<<10); i++){
int tmp = i;
judge[i] = 0;
while(tmp){
if(tmp & 1) judge[i]++;
tmp >>= 1;
}
}
while(scanf("%d%d%d", &n, &m, &k) == 3){
if(!n && !m && !k) break;
AC.init();
for(int i=0; i<m; i++){
scanf("%s", str);
AC.Insert(str, i);
}
AC.build();
AC.solve();
}
return 0;
}
题意:给n的DNA序列和一个待修改的DNA序列,求这的DNA序列通过调换位置能最多包含多少个给定的DNA序列(包含多次重复计算)
丧病的题。。。大家还是去看kuangbin大神的题解http://www.cnblogs.com/kuangbin/p/3163648.html我也是照着敲的=。=
//#pragma comment(linker,"/STACK:1024000000,1024000000")
#include <map>
#include <set>
#include <cmath>
#include <queue>
#include <stack>
#include <cstdio>
#include <string>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef double DB;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> PII;
#define pb push_back
#define MP make_pair
#define lson l, m, rt << 1
#define rson m + 1, r, rt << 1 | 1
const double eps = 1e-6;
const int inf = 0x3f3f3f3f;
const int mod = 100000;
const int maxn = 1000 + 10;
int n, cas = 0;
char str[maxn];
const int max_chd = 4;///字母表大小
struct AC_automata{
int chd[maxn][max_chd], fail[maxn], cnt[maxn];///trie中的chd数组,fail指针,节点标记(是多少个串的结尾)
int root, sz;///根节点,trie树大小
int newnode(){///建立新的节点,返回节点编号
for(int i=0; i<max_chd; i++) chd[sz][i] = -1;
cnt[sz++] = 0;
return sz - 1;
}
void init(){
sz = 0;
root = newnode();
}
int ID(char ch){
if(ch == 'A') return 0;
if(ch == 'T') return 1;
if(ch == 'G') return 2;
if(ch == 'C') return 3;
}
void Insert(char * str){///建trie树
int cur = root;
int len = strlen(str);
for(int i=0; i<len; i++){
if(chd[cur][ID(str[i])] == -1)
chd[cur][ID(str[i])] = newnode();
cur = chd[cur][ID(str[i])];
}
cnt[cur]++;
}
void build(){///构建fail指针
queue<int> Q;
while(!Q.empty()) Q.pop();
fail[root] = root;
for(int i=0; i<max_chd; i++){
if(chd[root][i] == -1)
chd[root][i] = root;
else{
fail[chd[root][i]] = root;
Q.push(chd[root][i]);
}
}
while(!Q.empty()){
int now = Q.front(); Q.pop();
cnt[now] += cnt[fail[now]];
for(int i=0; i<max_chd; i++){
if(chd[now][i] == -1)
chd[now][i] = chd[fail[now]][i];
else {
fail[chd[now][i]] = chd[fail[now]][i];
Q.push(chd[now][i]);
}
}
}
}
int num[4], bit[4], dp[maxn][11*11*11*11+10];
int Query(char * str){ //这是谁想到的这么丧病的做法。。。。
memset(num, 0, sizeof(num));
for(int i=0; str[i]; i++)
num[ID(str[i])]++;
bit[0] = (num[1] + 1) * (num[2] + 1) * (num[3] + 1);
bit[1] = (num[2] + 1) * (num[3] + 1);
bit[2] = num[3] + 1;
bit[3] = 1;
memset(dp, -1, sizeof(dp));
dp[0][0] = 0;
for(int A=0; A<=num[0]; A++)
for(int T=0; T<=num[1]; T++)
for(int G=0; G<=num[2]; G++)
for(int C=0; C<=num[3]; C++){
int cur = A * bit[0] + T * bit[1] + G * bit[2] + C * bit[3];
for(int i=0; i<sz; i++)
if(dp[i][cur] >= 0){
for(int j=0; j<4; j++){
if(j == 0 && A == num[0]) continue;
if(j == 1 && T == num[1]) continue;
if(j == 2 && G == num[2]) continue;
if(j == 3 && C == num[3]) continue;
dp[chd[i][j]][cur + bit[j]] = max(dp[chd[i][j]][cur + bit[j]], dp[i][cur] + cnt[chd[i][j]]);
}
}
}
int cur = bit[0] * num[0] + bit[1] * num[1] + bit[2] * num[2] + bit[3] * num[3];
int ans = -1;
for(int i=0; i<sz; i++)
ans = max(ans, dp[i][cur]);
return ans;
}
// Matrix getMatrix(){
// Matrix res = Matrix(sz + 1);
// for(int i=0;i<sz;i++)
// for(int j=0;j<max_chd;j++)
// if(cnt[chd[i][j]] == 0)///若为1则从i不能走到chd[i][j],否则串中就会含有trie树中的串
// res.mat[i][chd[i][j]]++;///i到chd[i][j]的路径条数
// return res;
// }
}AC;
int main(){
while(scanf("%d", &n) == 1 && n){
AC.init();
for(int i=0; i<n; i++){
scanf("%s", str);
AC.Insert(str);
}
AC.build();
scanf("%s", str);
printf("Case %d: %d\n", ++cas, AC.Query(str));
}
return 0;
}
题意:给n的资源串和m个病毒串。求一个串,使它包含所有的资源串而不包含病毒串,求这个串的最小长度。
首先把病毒串和资源串做上不同的标记放到AC自动机中。BFS求出资源串尾节点之间的距离。
然后dp[i][j]表示资源串的状态为i,以j资源串结尾的最小长度。
//#pragma comment(linker,"/STACK:1024000000,1024000000")
#include <map>
#include <set>
#include <cmath>
#include <queue>
#include <stack>
#include <cstdio>
#include <string>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef double DB;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> PII;
#define pb push_back
#define MP make_pair
#define lson l, m, rt << 1
#define rson m + 1, r, rt << 1 | 1
const double eps = 1e-6;
const int inf = 0x3f3f3f3f;
const int mod = 100000;
const int maxn = 1000000 + 10;
int n, m;
char str[maxn];
const int max_chd = 2;///字母表大小
struct AC_automata{
int chd[maxn][max_chd], fail[maxn], cnt[maxn];///trie中的chd数组,fail指针,节点标记(是多少个串的结尾)
int root, sz;///根节点,trie树大小
int newnode(){///建立新的节点,返回节点编号
for(int i=0; i<max_chd; i++) chd[sz][i] = -1;
cnt[sz++] = 0;
return sz - 1;
}
void init(){
sz = 0;
root = newnode();
}
int ID(char ch){
return ch - '0';
}
void Insert(char * str, int id){///建trie树
int cur = root;
int len = strlen(str);
for(int i=0; i<len; i++){
if(chd[cur][ID(str[i])] == -1)
chd[cur][ID(str[i])] = newnode();
cur = chd[cur][ID(str[i])];
}
cnt[cur] = id;
}
void build(){///构建fail指针
queue<int> Q;
while(!Q.empty()) Q.pop();
fail[root] = root;
for(int i=0; i<max_chd; i++){
if(chd[root][i] == -1)
chd[root][i] = root;
else{
fail[chd[root][i]] = root;
Q.push(chd[root][i]);
}
}
while(!Q.empty()){
int now = Q.front(); Q.pop();
cnt[now] += cnt[fail[now]];
for(int i=0; i<max_chd; i++){
if(chd[now][i] == -1)
chd[now][i] = chd[fail[now]][i];
else {
fail[chd[now][i]] = chd[fail[now]][i];
Q.push(chd[now][i]);
}
}
}
}
int d[maxn], mat[11][11], sour[11], tot;
queue<int> Q;
void bfs(){
sour[0] = 0; tot = 1;///trie树的根节点一定要访问
memset(mat, -1, sizeof(mat));///mat[i][j] = 资源串i和资源串j结尾的最短路
for(int i=0; i<sz; i++)
if(cnt[i] > 0) sour[tot++] = i;
for(int i=0; i<tot; i++){
int u = sour[i];
while(!Q.empty()) Q.pop();
memset(d, -1, sizeof(d));
d[u] = 0;
Q.push(u);
while(!Q.empty()){
int now = Q.front(); Q.pop();
for(int j=0; j<2; j++){
int tmp = chd[now][j];
if(cnt[tmp] >= 0 && d[tmp] < 0){///不是病毒并且以前没走过就可以走
d[tmp] = d[now] + 1;
Q.push(tmp);
}
}
}
for(int j=0; j<tot; j++)
mat[i][j] = d[sour[j]];
}
}
int dp[1 << 11][11];///资源串状态为i,以第j个资源串结尾的最短长度
int Query(){
bfs();
memset(dp, 0x3f, sizeof(dp));
dp[0][0] = 0;
for(int i=0; i<(1<<n); i++){
for(int j=0; j<tot; j++) if(dp[i][j] < inf){
for(int k=0; k<tot; k++)
if(j != k && mat[j][k] > 0){
dp[i | cnt[sour[k]]][k] = min(dp[i | cnt[sour[k]]][k], dp[i][j] + mat[j][k]);
}
}
}
int ans = inf;
for(int i=0; i<tot; i++)
ans = min(ans, dp[(1<< n) - 1][i]);
return ans;
}
}AC;
int main(){
while(scanf("%d%d", &n, &m) == 2){
if(!n && !m) break;
AC.init();
for(int i=0; i<n; i++){
scanf("%s", str);
AC.Insert(str, 1 << i);
}
for(int i=0; i<m; i++){
scanf("%s", str);
AC.Insert(str, -1);
}
AC.build();
printf("%d\n", AC.Query());
}
return 0;
}