• 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
  • 相关阅读:
    安装VC6.0遇到的问题
    开发、测试环境
    OPENGL绘制文字
    C++实现文件关联
    MFC多国语言——配置文件
    MFC 资源记录
    如何解决——汉化英文界面出现乱码
    项目配置——添加第三方资源
    队列&生产者消费者模型
    抢票小程序
  • 原文地址:https://www.cnblogs.com/CzxingcHen/p/9590963.html
Copyright © 2020-2023  润新知