UVALive 6507 Passwords 哈希+无序map

题意:

有n个字符串,每个字符串长度不超过20,000。让你找出两个不同字符串中所隐藏的前缀字符串v,和后缀字符串w。

然后使得v连接|w|次等于w连接|v|次。|v|表示字符串v的长度。

输出|v|,|w|的结果。

如果有多种方案,让你输出两者和最大的。


思路:

hash+umap

看杰哥代码的。

将每个字符串都hash,并将它们的每个前缀字符串hash值都插入到umap中。

枚举每个字符串的后缀,检查以每个后缀作为基准的可行性,并将|v|,|w|最大化。

检查可行性:

找两个值来保存脑补前缀v和实际后缀w的hash值,一开始v和w是一样的。

然后通过查找umap中是否存在v的hash值来更新|v|的长度以及比较v和w来更新|w|的长度。

具体看代码。


code:

#include <bits/stdc++.h>
using namespace std;

const int N = 205;
const int L = 2e4+5;
const int X = 233;
const int MOD = 1e9+7;
typedef long long LL;

int n;
char ch[N][L];
int hh[N][L];
int ax[L];
unordered_map <int, int> ump;

int get(int i, int l, int r) {
    int len = r-l+1;
    return (hh[i][l+len-1]-(LL)hh[i][l-1]*ax[len]%MOD+MOD)%MOD;
}
void solve() {
    ump.clear();
    for(int i = 0;i < n; i++) {
        int len = strlen(ch[i]+1);
        for(int j = 1;j <= len; j++) {
            hh[i][j] = ((LL)hh[i][j-1]*X%MOD+ch[i][j])%MOD;
            ump[hh[i][j]]++;
        }
    }
    int r1 = 0, r2 = 0;
    for(int i = 0;i < n; i++) {
        int len = strlen(ch[i]+1);
        for(int j = 1;j <= len; j++) ump[hh[i][j]]--;
        for(int j = 1;j <= len; j++) {
            int h = get(i, len-j+1, len);   
            int p = h;//p为脑补前缀v的hash值
            if(!ump[p]) continue;
            int t1 = 1, t2 = 1;
            for(int k = 2;k*j <= len; k++) {
                int tmp = get(i, len-k*j+1, len);//实际后缀w的hash值
                p = ((LL)p*ax[j]%MOD+h)%MOD;//更新v的hash值,相当于把最开始的后缀加一个前面
                if(tmp != p) break;//如果两者不相等,说明已经不能走下去
                if(ump[p]) t1 = k;//umap中找到了,说明可以更新|v|的长度
                t2 = k;
            }
            if(t1 == t2 && t1 == 1) continue;
            if(t1 == t2) t1--;
            if(t1*j + t2*j > r1+r2) {
                r1 = t1*j;
                r2 = t2*j;
            }
        }
        for(int j = 1;j <= len; j++) ump[hh[i][j]]++;
    }
    printf("%d %d\n", r1, r2);
}
        
int main() {
    ax[0] = 1;
    for(int i = 1;i < L; i++) ax[i] = (LL)ax[i-1]*X%MOD;
    int T;
    scanf("%d", &T);
    while(T--) {
        scanf("%d", &n);
        for(int i = 0;i < n; i++) scanf("%s", ch[i]+1);
        solve();
    }
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值