SYZOJ#186 [额]你猜是不是DP

[额]你猜是不是DP

传送门

题目描述

现在给两个仅包含小写字母的字符串a,b ,求a 与b的最长公共连续子串的长度。

输入格式

两个字符串

输出格式

一个整数,为输入的两个字符串的最长公共连续子串的长度

输入

qaqaaaq
qqaqa

输出

4

解释

最长连续公共子串为qaqa,长度为4


这道题我们只需要把两个字符串的每一个前缀的哈希值求出来,然后二分公共串的长度,如果当前二分到的这个长度 l l l可以找到公共子串,那么我们一定可以找到 l − 1 l-1 l1 l − 2 l-2 l2,……, 1 1 1的公共子串,所以我们往 m i d + 1 → r mid+1 \to r mid+1r二分,其他的情况用 l → m i d − 1 l \to mid-1 lmid1二分,找到一个合适的答案就保存着,这样可以保证我们最后求出来的答案是最大的。

#include <cstdio>
#include <cstdlib>
#include <bits/stdc++.h>
using namespace std;
#define ULL unsigned long long
#define MAXN 200003 
ULL xp[MAXN];
ULL hasha[MAXN], hashb[MAXN];
ULL ha[MAXN], hb[MAXN];
char A[MAXN], B[MAXN];
int x = 233;
int la, lb, mlen, minlen;
bool check(int len)
{
    
    
    for(int i = 0; i < la - len + 1; i++)
        hasha[i] = ha[i] - ha[i + len] * xp[len];
    for(int i = 0; i < lb - len + 1; i++)
        hashb[i] = hb[i] - hb[i + len] * xp[len];
    sort(hashb, hashb + lb - len + 1);
    for(int i = 0; i < la - len + 1; i++)
    {
    
    
        ULL h = hasha[i];
        int p = lower_bound(hashb, hashb + lb - len + 1, h) - hashb;
        if(p < lb - len + 1 && h == hashb[p])
			return true;
    }
    return false;
}
int main()
{
    
    
    scanf("%s %s", A, B);
    la = strlen(A), lb = strlen(B);
    mlen = max(la, lb), minlen = min(la, lb);
    for(int i = la - 1; i >= 0; i--)
        ha[i] = ha[i + 1] * x + A[i];   //哈希前缀
    for(int i = lb - 1; i >= 0; i--)
        hb[i] = hb[i + 1] * x + B[i];   //哈希前缀
	xp[0] = 1;
	for(int i = 1; i <= mlen; i++)
		xp[i] = xp[i - 1] * x;      //前缀
    int l = 0, r = minlen;
    int ans;
    while(l <= r)    //二分长度
    {
    
    
        int m = (l + r) >> 1;
        if(check(m))
        {
    
    
			ans = m;      //每找到有一个合适的答案就保存下来
			l = m + 1;
    	}
		else
            r = m - 1;
    }
    printf("%d\n", ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/CoderZeng/article/details/109066080