1. 程式人生 > >【bzoj2085】[Poi2010]Hamsters Hash+倍增Floyd

【bzoj2085】[Poi2010]Hamsters Hash+倍增Floyd

per nbsp amp mon 多次 highlight 需要 spa bernard

題目描述

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;
}

【bzoj2085】[Poi2010]Hamsters Hash+倍增Floyd