NOIP2001 统计单词个数 rabin-karp哈希

1233: NOIP2001:统计单词个数

时间限制: 0 Sec  内存限制: 128 MB
提交: 8  解决: 6
[上一题][提交][讨论版][状态][下一题]

题目描述

        给出一个长度不超过200的由小写英文字母组成的字母串(约定;该字串以每行20个字母的方式输入,且保证每行一定为20个)。要求将此字母串分成k份(1< k< =40),且每份中包含的单词个数加起来总数最大(每份中包含的单词可以部分重叠。当选用一个单词之后,其第一个字母不能再用。例如字符串this中可包含this和is,选用this之后就不能包含th)。

        单词在给出的一个不超过6个单词的字典中。

        要求输出最大的个数。

输入

第一行有二个正整数(p,k)

p表示字串的行数;k表示分为k个部分。

接下来的p行,每行均有20个字符。

再接下来有一个正整数s,表示字典中单词个数。(1< =s< =6)

接下来的s行,每行均有一个单词。

输出

输出一个整数,即最大的个数

样例输入

1 3
thisisabookyouareaoh
4
is
a
ok
sab

样例输出

7

提示

NOIP2001提高组第三题

我用的是主流dp,所以这里不讲dp, 讲如何处理字符串。

我用的是rabin-karp哈希

记前 i 位的哈希值 为 hx[i]

搞一个base, 我的是131

hx[i] = (hx[i-1] * base + s[i]) % mod P

P是个大素数, 我选的是1e9+7

这样我们的hx[i]就等于 s[1]*base^i-1 + s[2]*base^i-2 + ..... s[i]*base^0

我们要求l, r的哈希值, 观察一下hx[l-1] 和 hx[r]

hx[l-1] = s[1]*base^l-2 + s[2]*base^l-3 + .....s[l-1]*base^0

hx[r] = s[1]*base^r-1 + s[2]*base^r-2 + .....s[r]*base^0

我们只看s[1]的指数就行了(后面一样的)

于是我们将hx[l-1]乘上 base^r-l+1次方, 用hx[r]一减, 从s[1]到s[l-1]的值全被消掉了

我们就得到了 [l, r]的哈希值

然后就dp就行了

dp也有一个坑的地方, 因为一定要分成k份, 不能 < k份, 所以我们在 f[t][j-1] 有值的时候才转移

代码如下:

#include <cstdio>
#include <string.h>
#define ll long long
#define min(a, b) ((a) < (b) ? (a) : (b))
#define max(a, b) ((a) > (b) ? (a) : (b))
#define base 131
#define mod 1000000007
ll hx[205], re[205], po[205], t, hav[205][205], s, l[7];
int f[205][205];
char a[205], b[25], c[1005];
int len;
ll gethash(int l, int r){
    return ((hx[r] - po[r-l+1]*hx[l-1])%mod+mod) % mod;
}
int get(int x, int y){
    int i;
    for(i = 1; i <= s; i++) if(gethash(x, min(y, x+l[i]-1)) == re[i]) return 1;
    return 0;
}
int main(){
    int i, j, p, k, n;
    scanf("%d%d", &p, &k);
    scanf("%s", a+1);
    len = strlen(a+1);
    for(i = 2; i <= p; i++){
        scanf("%s", b+1);
        n = strlen(b+1);
        for(j = len+1; j <= len+n; j++) a[j] = b[j-len];
        len += n;
    }
    for(i = po[0] = 1; i <= len; i++){
        po[i] = po[i-1] * base % mod;
        hx[i] = (hx[i-1] * base + a[i]) % mod;
    }
    scanf("%d", &s);
    for(i = 1; i <= s; i++){
        scanf("%s", c+1);
        n = strlen(c+1);
        l[i] = n;
        t = 0;
        for(j = 1; j <= n; j++) t = (t * base + c[j]) % mod;
        re[i] = t;
    }
    for(i = len; i >= 1; i--){
        for(j = i; j >= 1; j--){
            hav[j][i] = hav[j+1][i];
            hav[j][i] += get(j, i);
        }
    }
    for(i = 1; i <= len; i++){
        f[i][1] = hav[1][i];
        for(j = 2; j <= k; j++){
            for(t = 1; t < i; t++){
                if(f[t][j-1]) f[i][j] = max(f[i][j], f[t][j-1] + hav[t+1][i]);
            }
        }
    }
    printf("%d", f[len][k]);
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值