【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\n" , ans);
    return 0;
}

 

 

posted @ 2017-08-18 18:52  GXZlegend  阅读(322)  评论(0编辑  收藏  举报