zoukankan      html  css  js  c++  java
  • 【bzoj2085】[Poi2010]Hamsters Hash+倍增Floyd

    题目描述

    Tz养了一群仓鼠,他们都有英文小写的名字,现在Tz想用一个字母序列来表示他们的名字,只要他们的名字是字母序列中的一个子串就算,出现多次可以重复计算。现在Tz想好了要出现多少个名字,请你求出最短的字母序列的长度是多少。n个字符串保证不互相包含。

    输入

    输入:第一行n(1<=n<=200)和m(1<=m<=10的9此方),n表示有多少个仓鼠,m表示Tz希望出现名字的次数,接下来n行,每行都是仓鼠的名字(中间没有空格)。

    输出

    输出:一行,最短的字母序列的长度。

    样例输入

    4 5
    monika
    tomek
    szymon
    bernard

    样例输出

    23


    题解

    Hash+倍增Floyd

    由于n只有200,并且任意两串不包含。所以可以预处理出某个串后还需要加几个字符可以变成另一个串,可以使用Hash解决。

    然后题目要求出现总数为m,相当于要经过m-1个点的最短路径,使用倍增Floyd快速幂求出。

    最后的答案为 原串长+最短路 的最小值。

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    typedef unsigned long long ull;
    char str[100010];
    ull hash[100010] , base[100010];
    int lp[210] , rp[210] , n;
    struct data
    {
        ull v[210][210];
        data() {memset(v , 0x3f , sizeof(v));}
        data operator*(const data &a)const
        {
            data ans;
            int i , j , k;
            for(k = 1 ; k <= n ; k ++ )
                for(i = 1 ; i <= n ; i ++ )
                    for(j = 1 ; j <= n ; j ++ )
                        ans.v[i][j] = min(ans.v[i][j] , v[i][k] + a.v[k][j]);
            return ans;
        }
    }a , ret;
    data pow(data x , int y)
    {
        data ans;
        int i;
        for(i = 1 ; i <= n ; i ++ ) ans.v[i][i] = 0;
        while(y)
        {
            if(y & 1) ans = ans * x;
            x = x * x , y >>= 1;
        }
        return ans;
    }
    int main()
    {
        int m , i , j , k;
        ull ans = 1ull << 63;
        scanf("%d%d" , &n , &m);
        for(i = 1 ; i <= n ; i ++ )
            lp[i] = rp[i - 1] + 1 , scanf("%s" , str + lp[i]) , rp[i] = strlen(str + lp[i]) + lp[i] - 1;
        base[0] = 1;
        for(i = 1 ; i <= rp[n] ; i ++ )
            base[i] = base[i - 1] * 2333 , hash[i] = hash[i - 1] * 2333 + str[i];
        for(i = 1 ; i <= n ; i ++ )
            for(j = 1 ; j <= n ; j ++ )
                for(k = 0 ; k < rp[i] - lp[i] + 1 && k < rp[j] - lp[j] + 1 ; k ++ )
                    if(hash[rp[i]] - hash[rp[i] - k] * base[k] == hash[lp[j] + k - 1] - hash[lp[j] - 1] * base[k])
                        a.v[i][j] = rp[j] - lp[j] + 1 - k;
        ret = pow(a , m - 1);
        for(i = 1 ; i <= n ; i ++ )
            for(j = 1 ; j <= n ; j ++ )
                ans = min(ans , rp[i] - lp[i] + 1 + ret.v[i][j]);
        printf("%llu
    " , ans);
        return 0;
    }
    

     

  • 相关阅读:
    CSU 1554 SG Value (集合类的学习)
    CSUOJ 1542 线段树解决括号反向问题
    POJ 1679 判最小生成树的不唯一性 或 利用次小生成树求解
    HDU1074 Doing Homework 状态压缩dp
    POJ 2479 两段连续最大和
    HDU1024 多段最大和 DP
    HDU 4803 贪心
    POJ 3469 网络流最小割
    SPOJ ARCTAN
    COJ 1163 乘法逆元的求解
  • 原文地址:https://www.cnblogs.com/GXZlegend/p/7391272.html
Copyright © 2011-2022 走看看