bzoj3172: [Tjoi2013]单词

题面在这里

题意:

给n个单词,问每个单词在所有单词中出现了多少次。

做法:

后缀数组。(ac自动机也可以qwq)
首先把所有串用不同的字符拼接起来,跑大串的sa。
然后对于每一个单词,假设它的长度为len,暴力找到h[i]>=len的最左边和最右边的位置l,r,然后这个单词出现的次数就是这一段的长度r-(l-1)+1。
(其实暴力应该是水过的?qaq,正解大概搞个二分什么的就行。。反正暴力能过qaq。。1000+ms)

代码:

/*************************************************************
    Problem: bzoj 3172 [Tjoi2013]单词
    User: fengyuan
    Language: C++
    Result: Accepted
    Time: 1392 ms
    Memory: 50120 kb
    Submit_Time: 2018-01-24 08:46:04
*************************************************************/

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cctype>
#include<cstdlib>
#include<cmath>
using namespace std;
typedef long long ll;

const int N = 2000000;
int n, m, all;
int rk[N], tp[N], sa[N], h[N], len[210], tong[N], pos[210], s[N];
char str[N];

inline void ssort() {
    for(int i = 0; i <= all; i ++) tong[i] = 0;
    for(int i = 1; i <= n; i ++) tong[rk[tp[i]]] ++;
    for(int i = 1; i <= all; i ++) tong[i] += tong[i-1];
    for(int i = n; i >= 1; i --) sa[tong[rk[tp[i]]] --] = tp[i];
}
inline void get_sa() {
    for(int i = 1; i <= n; i ++) rk[i] = s[i], tp[i] = i;
    all = 500; ssort(); all = 1; int w = 1;
    while(all < n) {
        int t = 0;
        for(int i = n-w+1; i <= n; i ++) tp[++ t] = i;
        for(int i = 1; i <= n; i ++) if(sa[i] > w) tp[++ t] = sa[i]-w;
        ssort(); for(int i = 1; i <= n; i ++) tp[i] = rk[i];
        rk[sa[1]] = all = 1;
        for(int i = 2; i <= n; i ++)
            rk[sa[i]] = (tp[sa[i]] == tp[sa[i-1]] && tp[sa[i]+w] == tp[sa[i-1]+w])?all:++ all;
        w <<= 1;
    } int k = 0;
    for(int i = 1; i <= n; i ++) {
        if(k) k --; int j = sa[rk[i]-1];
        for(; i+k <= n && j+k <= n && s[i+k] == s[j+k]; k ++);
        h[rk[i]] = k;
    }
}
int main() {
    scanf("%d", &m);
    for(int i = 1; i <= m; i ++) {
        scanf("%s", str); len[i] = strlen(str); pos[i] = n+1;
        for(int j = 0; j < len[i]; j ++) s[++ n] = str[j];
        s[++ n] = 'z'+i;
    }
    get_sa();
    for(int i = 1; i <= m; i ++) {
        int x = rk[pos[i]], j, lft, rgt; j = x;
        while(j && h[j] >= len[i]) j --;
        lft = j+1; j = x+1;
        while(j <= n && h[j] >= len[i]) j ++;
        rgt = j-1;
        printf("%d\n",  rgt-(lft-1)+1);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值