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;
}
posted @ 2020-08-14 14:33  limil  阅读(106)  评论(0编辑  收藏  举报