BZOJ2085 - Hamsters(kmp+倍增floyd)
阿新 • • 發佈:2020-08-14
題目
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; }