• 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}$   $(iin s, j otin 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("
    ");
        for(int i = 1; i <= n; i++)
            printf("%s
    ", a[i].s + 1);
        printf("
    ");
        for(int i = 1; i <= n; i++, printf("
    "))
            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("
    ");
            for(int j = 1; j <= ans; j++) putchar(str2[j]); printf("
    ");   */
            
            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("
    ");
        return 0;
    }
    View Code
  • 相关阅读:
    1289大鱼吃小鱼(STL中栈的应用)
    1347旋转字符串
    2133排队接水——优先队列
    7-37 整数分解为若干项之和(20 分)
    有一个VC的bug:非标准语法
    指针的一点点用法
    好久没写的博客_数组的长度等小问题
    解决strcmp的错误以及VS的快捷键
    输入流和注释
    VS出现异常?!和十进制转二进制比是小事
  • 原文地址:https://www.cnblogs.com/CzxingcHen/p/9590963.html
Copyright © 2020-2023  润新知