【算法题解】Acwing 最长公共子串(3508)

题目描述

给定两个字符串,求这两个字符串的不包含数字的最长公共子串的长度。

输入格式
共两行,每行一个由小写字母和数字构成的字符串。

输出格式
一个整数,表示给定两个字符串的不包含数字的最长公共子串的长度。

如果不存在满足要求的非空公共子串,则输出 0

数据范围
输入字符串的长度均不超过 10000

样例
输入样例:
ab123abccff
abcfacb123
输出样例:
3

(二分 + 字符串哈希) O ( n l o g n ) O(nlogn) O(nlogn)

由求最大子串我们可以分析出二段性用二分法,然后我们思考如何判断某子串是否为公共子串;先想朴素做法,将第一个字符串的每一个子串存到hash表中(O(mn),m为二分法枚举的子串长度),而二分法判断为O(logn),总时间复杂度为O(n^2logn);
数据n的最大值为1e4,会tle.
所以我们要想办法优化,我们可以想到用字符串哈希将每个子串映射成数字存到哈希表里(O(n)),二分为O(logn),总时间复杂度为(O(nlogn)),能ac.

C++ 代码
#include <iostream>
#include <cstring>
#include <algorithm>
#include <unordered_set>

using namespace std;
typedef unsigned long long ULL;
const int P = 131;
const int N = 20010;
int n,m;
ULL h[N],p[N];
ULL get(int l,int r){
    return h[r] - h[l - 1] * p[r - l + 1];
}
bool check(int mid){
    unordered_set<ULL> s;
    for(int i = 1;i + mid - 1 <= n;i ++){
        s.insert(get(i,i + mid - 1));
    }
    for(int i = n + 1;i + mid - 1 <= n + m;i ++){
        if(s.count(get(i,i + mid - 1))) return true;
    }
    return false;
}
int main()
{
    char str[N];
    scanf("%s",str +1);
     n = strlen(str + 1);
    scanf("%s",str + n + 1);
     m = strlen(str + n + 1);
    p[0] = 1;
    for(int i = 1;i <= n + m;i ++){
        p[i] = p[i - 1] * P;
        char c = str[i];
        if(isdigit(c)){
            if(i <= n) c = '#';
            else c = '@';
        }
        h[i] = h[i - 1] * P + c;
    }
    int l = 0,r = min(n,m);
    while(l < r){
        int mid = l + r + 1>> 1;
        if(check(mid)) l = mid;
        else r = mid - 1;
    }
    cout << l;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值