Topcoder 14531 Softmatch

题目链接

Vjudge Softmatch Topcoder 14531 Softmatch

题目大意

有一个长为 \(n\) 的主串 \(S\)\(K\) 个长度 \(\leq n\) 的模式串 \(\{p_i\}\)\(S\) 由 a 和 b 组成, \(p_i\) 由 1,2,3,4 组成,给定 \(S\) 串后我们可以选择其中一些字符改为大写,即 a->A 或 b->B,有如下的匹配规则:

a 匹配 0 和 1,A 匹配 2 和 3 。

b 匹配 0 和 2,B 匹配 1 和 3 。

\(T\)\(S\) 中的匹配次数为 \(f(T)\),求 \(\max\{\sum_{i=1}^K f(p_i)\}\)

\(1\leq n\leq 50\), \(1\leq K\leq 5\)

思路

\(O(n^4K^2)\) 做法

考虑 \(dp\),注意到 \(S\) 每一位的两种选择互为补集,如果我们已经确定了一个串在位置 \(i\) 处匹配了 \(k\) 位,那么便可以直接还原出 \(S\) 在那 \(k\) 位的大小写选择情况,由于如果要 \(dp\) 转移,我们是必须要知道 \(S\) 这附近几位是什么样子的,于是设计这样的 \(dp\) 状态:

\(dp_{i,j,k}\) 为当前考虑到了 \(S\) 的第 \(i\) 位,和 \(S\) 匹配长度最大的串是 \(p_j\),它的匹配长度是 \(k\) ,此时完成的匹配数量最大值。由于匹配长度最大,这 \(k\) 位已经包含了我们所有需要知道的信息,在到达这一状态时,把 \(S\) 的这 \(k\) 位还原出来,随后枚举下一位是选小写还是大写,把两种情况下的 \(k+1\) 位主串的后缀和 \(K\) 个模式串的前缀进行匹配,得到各自的最大匹配长度,然后就可以知道该决策下第 \(i+1\) 位完成了多少次匹配了。

状态数 \(O(n^2K)\),暴力转移一次是 \(O(n^2K)\) 的,所以时间复杂度为 \(O(n^4K^2)\)

 

\(O(n^3K^2)\) 做法

注意到上面每次的转移有重复可以利用的地方,有 \(O(nK)\) 个长度至多为 \(n\) 的确定的 \(S\) 片段,把他们预处理出来,再和 \(K\) 个串前后缀匹配,预处理 \(O(n^3K^2)\)\(dp\) 的时间复杂度降到了 \(O(n^2K^2)\)

 

\(O(n^3K)\) 做法

可以发现上面的做法还是有一点暴力的成分在里面的,复杂度瓶颈在于前后缀匹配这里。

不要考虑匹配长度这件事了,我们直接快进到匹配完成这件事,毕竟最终答案也只要完成匹配的数量,而匹配的最大数量也就 \(nK\) 个,背后最多对应着 \(O(nK)\)\(S\) 不同选择方式的片段。设 \(0\)\(S\) 这一位选择小写,\(1\) 为这一位选择大写,这样总共会有 \(nK\) 个 01串,将它们插入 \(AC\) 自动机里,重新来 \(dp\)

\(dp_{i,j}\) 为考虑到了 \(S\) 的第 \(i\) 位,目前位于 \(AC\) 自动机的节点 \(j\) 时的最大匹配数。先预处理出每个模式串在不同位置完成匹配对应 \(AC\) 自动机上的节点,设为 \(match_{i,j}\),另设 \(cnt_i\) 为第 \(j\) 个节点当前对应了多少个完成的匹配,\(cnt\) 很好计算,先在每个模式串直接匹配(即 \(match_{i,j}\))的节点记上一个 \(1\),然后由浅到深扫一遍 \(AC\) 自动机,每个节点再从其 \(fail\) 节点处转移一次即可。

于是就可以直接 \(dp\) 转移了!枚举下一位是 \(0\) 还是 \(1\),然后往下一个节点直接转移即可,设枚举的这一位是 \(p\),即

\[dp_{i,nxt_{j,p}}\leftarrow dp_{i-1,j}+cnt_{nxt_{j,p}} \]

\(AC\) 自动机共 \(O(n^2K)\) 个节点,\(dp\) 状态数 \(O(n^3K)\),转移 \(O(1)\)\(cnt\) 计算复杂度 \(O(n^3K)\),从而总时间复杂度 \(O(n^3K)\) 。在 \(1\leq N,K\leq 100\) 的数据上虽然理论计算量上限 \(1e8\),但实际上贴不到上限,\(code\) 实际表现极限数据 \(421\;ms\)

 

Code

题目原意可能是想要大家找到一种多项式时间复杂度的做法即可,但是深挖下去,其实优化的空间还挺多的!借助这个特性,我把它出成了模拟赛的题,设了很多档部分分,然而结果没有人在这题上获得 \(>0\) 的分数。。

#include<iostream>
#include<vector>
#define rep(i,a,b) for(int i = (a); i <= (b); i++)
#define per(i,b,a) for(int i = (b); i >= (a); i--)
#define N 55
#define K 6
#define Inf 0x3f3f3f3f
using namespace std;

struct Aho_Corasick_Automaton{
    struct node{
        int cnt, id;
        node *son[2], *fail;
        node(){
            son[0] = son[1] = fail = 0;
            cnt = id = 0;
        }
    } *root = new node, *q[N*N*K], *match[K][N];
    int cnt, automaton[N*N*K][2];

    node* insert(vector<int> choice){
        node *x = root;
        for(int k : choice){
            if(x->son[k] == 0) x->son[k] = new node;
            x = x->son[k];
        }
        return x;
    }

    void build(){
        q[1] = root, root->id = 1, cnt = 1;
        rep(i,0,1) if(root->son[i])
            root->son[i]->fail = root, q[++cnt] = root->son[i], q[cnt]->id = cnt;
        
        rep(i,2,cnt) rep(j,0,1) if(q[i]->son[j]){
            q[++cnt] = q[i]->son[j];
            node *nxt = q[i]->fail;
            while(nxt != root && nxt->son[j] == 0) nxt = nxt->fail;
            if(nxt->son[j]) nxt = nxt->son[j];
            q[cnt]->id = cnt, q[cnt]->fail = nxt;
        }

        rep(i,1,cnt) rep(j,0,1){
            if(q[i]->son[j]) automaton[i][j] = q[i]->son[j]->id;
            else{
                if(i == 1) automaton[1][j] = 1;
                else automaton[i][j] = automaton[q[i]->fail->id][j];
            }
        }
    }

    void clear(){ rep(i,1,cnt) q[i]->cnt = 0; }

    void prepare(){ rep(i,2,cnt) q[i]->cnt += q[i]->fail->cnt; }
} AC;

class Softmatch{
    public:
    int n, m, dp[N*N*K], pre[N*N*K];

    int make_choice(char a, char b){
        if(a == 'a') return b >= '2';
        else return b == '1' || b == '3';
    }

    bool Max(int &a, int b){ return a < b ? a = b, 1 : 0; }

    int count(string S, vector<string> patterns){
        n = S.size(), m = patterns.size();
        S = " "+S;
        rep(i,0,m-1){
            int len = patterns[i].size();
            rep(j,1,n-len+1){
                vector<int> choice;
                rep(k,0,len-1) choice.push_back(make_choice(S[j+k], patterns[i][k]));
                AC.match[i][j+len-1] = AC.insert(choice);
            }
        }
        AC.build();

        rep(i,1,AC.cnt) dp[i] = -Inf;
        dp[1] = 0;
        rep(i,1,n){
            AC.clear();
            rep(j,0,m-1) if(AC.match[j][i]) AC.match[j][i]->cnt++;
            AC.prepare();

            rep(j,1,AC.cnt) pre[j] = dp[j], dp[j] = -Inf;
            rep(j,1,AC.cnt) if(pre[j] >= 0){
                rep(k,0,1){
                    int nxt = AC.automaton[j][k];
                    Max(dp[nxt], pre[j]+AC.q[nxt]->cnt);
                }
            }
        }

        int ans = 0;
        rep(i,1,AC.cnt) Max(ans, dp[i]);
        return ans;
    }
} solve;

int main(){
    string a, b;
    vector<string> read;
    int k;
    cin>>a>>k;
    rep(i,1,k) cin>>b, read.push_back(b);
    cout<< solve.count(a, read) <<endl;
    return 0;
}
posted @ 2021-06-09 12:09  Neal_lee  阅读(78)  评论(0编辑  收藏  举报