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