1. 程式人生 > 實用技巧 >BZOJ2085 - Hamsters(kmp+倍增floyd)

BZOJ2085 - Hamsters(kmp+倍增floyd)

題目

Tz養了一群倉鼠,他們都有英文小寫的名字,現在Tz想用一個字母序列來表示他們的名字,只要他們的名字是字母序列中的一個子串就算,出現多次可以重複計算。現在Tz想好了要出現多少個名字,請你求出最短的字母序列的長度是多少。
(注:所有名字都不互相包含)

題解

兩兩單詞連邊,邊權為兩個單詞重疊連線後增加的長度,可以用kmp來求。

所以問題就可以轉化成在這個圖上走m-1條的最短路徑,用倍增floyd。

#include <bits/stdc++.h>

#define endl '\n'
#define IOS std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
#define FILE freopen(".//data_generator//in.txt","r",stdin),freopen("res.txt","w",stdout)
#define FI freopen(".//data_generator//in.txt","r",stdin)
#define FO freopen("res.txt","w",stdout)
#define pb push_back
#define mp make_pair
#define seteps(N) fixed << setprecision(N) 
typedef long long ll;

using namespace std;
/*-----------------------------------------------------------------*/

ll gcd(ll a, ll b) {return b ? gcd(b, a % b) : a;}
#define INF 1e18

const int N = 210;
const double eps = 1e-5;
ll dis[N][N];
char s[N][30000];
int nt[30000];
 
struct Floyd { //*運算子相當於作一次最短路運算
    static const ll inf;
    ll dis[N][N];
    int n;
    Floyd(int n, bool v = 1) : n(n) { //v: 1:inf, 0:0
        for(int i = 1; i <= n; i++) {
            for(int j = 1; j <= n; j++) {
                dis[i][j] = v ? inf : 0;
            }
        }
    }
    Floyd operator *(const Floyd & rhs) {
        Floyd tmp(n);
        for(int i = 1; i <= n; i++) {
            for(int j = 1; j <= n; j++) {
                for(int k = 1; k <= n; k++) {
                    tmp.dis[i][j] = min(tmp.dis[i][j], rhs.dis[i][k] + dis[k][j]); 
                }
            }
        }
        return tmp;
    }
    void print() {
        for(int i = 1; i <= n; i++) {
            for(int j = 1; j <= n; j++) {
                cout << dis[i][j] << " ";
            }
            cout << endl;
        }
    }
    Floyd walk(int b) {
        Floyd res(n, 0);
        Floyd a(*this);
        while(b) {
            if(b & 1) {
                res = res * a; //矩陣相乘順序不能交換
            }
            a = a * a;
            b = b >> 1;
        }
        return res;
    }
};
const ll Floyd::inf = 1e18;

void getnext(int p) {
    int i = 0,j = -1;
    nt[i] = j;
    while(s[p][i]) {
        if(j == -1 || s[p][i] == s[p][j]) {
            i++, j++;
            nt[i] = j;
        } else {
            j = nt[j];
        }
    }
}

int getlen(int a, int b) {
    int i = 1, j = 0;
    while(s[a][i]) {
        if(j == -1 || s[a][i] == s[b][j]) {
            i++, j++;
            if(!s[b][j]) break;
        } else {
            j = nt[j];
        }
    }
    return strlen(s[b]) - j;
}

int main() {
    IOS;
    int n, m;
    cin >> n >> m;
    for(int i = 1; i <= n; i++) {
        cin >> s[i];
    }
    Floyd ans(n);
    for(int i = 1; i <= n; i++) {
        for(int j = 1; j <= n; j++) {
            getnext(j);
            ans.dis[i][j] = getlen(i, j);
            
        }
    }
    ans = ans.walk(m - 1);
    ll tans = INF;
    for(int i = 1; i <= n; i++) {
        for(int j = 1; j <= n; j++) {
            tans = min(tans, ans.dis[i][j] + (ll)strlen(s[i]));
        }
    }
    cout << tans << endl;
}