AC自动机 + DP小结

3 篇文章 0 订阅
2 篇文章 0 订阅

HDU--2457 DNA repair

题意:给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;
}


HDU -- 2296 Ring

题意:给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;
}


HDU -- 3341 Lost's revenge

题意:给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;
}


HDU -- 3247 Resource Archiver

题意:给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;
}



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值