SYZOJ 186 [额]你猜是不是DP(哈希+二分答案+二分搜索)
题目描述
现在给两个仅包含小写字母的字符串a,b ,求a 与b的最长公共连续子串的长度。
输入格式
两个字符串
输出格式
一个整数,为输入的两个字符串的最长公共连续子串的长度
测试样例
输入
qaqaaaq
qqaqa
输出
4
解释
最长连续公共子串为qaqa,长度为4
数据范围与提示
20 组测试数据
对于20%的数据,a,b长度 ∈ [1, 200]
对于50%的数据,a,b长度 ∈ [1, 20000]
对于100%的数据, a,b长度 ∈ [1, 200000]
题目链接:SYZOJ 186
哈希第一题,预处理出前缀hash值后二分答案,这里需要用二分优化,把第一个和第二个字符串的所有以mid为长度的区间哈希值储存并对后者排序,那么只要遍历第一个字符串的区间哈希值的时候二分查找第二段是否存在同样的哈希值即可。若二分+双for循环遍历的复杂度$O(logN*N^2)$只有85分会超时,改成二分搜索复杂度就成了$O(logN*NlogN)$就不会超时了,突然发现哈希有时候是个神器,只是写起来有点麻烦而且对于hash killer这种题目就比较尴尬了
代码:
#include <stdio.h> #include <bits/stdc++.h> using namespace std; #define INF 0x3f3f3f3f #define LC(x) (x<<1) #define RC(x) ((x<<1)+1) #define MID(x,y) ((x+y)>>1) #define CLR(arr,val) memset(arr,val,sizeof(arr)) #define FAST_IO ios::sync_with_stdio(false);cin.tie(0); typedef pair<int, int> pii; typedef long long LL; typedef unsigned long long ULL; const double PI = acos(-1.0); const int N = 200010; const ULL seed = 1e9 + 7; ULL prefix[N], pa[N], pb[N], ha[N], hb[N]; char a[N], b[N]; ULL vec[N]; void init() { prefix[0] = 1ull; for (int i = 1; i < N; ++i) prefix[i] = prefix[i - 1] * seed; } int main(void) { int i, j; init(); while (~scanf("%s%s", a + 1, b + 1)) { int la = strlen(a + 1); int lb = strlen(b + 1); pa[0] = pb[0] = 0; for (i = 1; i <= la; ++i) pa[i] = pa[i - 1] * seed + a[i]; for (i = 1; i <= lb; ++i) pb[i] = pb[i - 1] * seed + b[i]; int ans = 0, L = 0, R = min(la, lb); while (L <= R) { int mid = (L + R) >> 1; int flag = 0; int sa = 0, sb = 0; for (i = mid; i <= la; ++i) ha[sa++] = pa[i] - pa[i - mid] * prefix[mid]; for (i = mid; i <= lb; ++i) hb[sb++] = pb[i] - pb[i - mid] * prefix[mid]; sort(hb, hb + sb); for (i = 0; i < sa; ++i) { if (binary_search(hb, hb + sb, ha[i])) { flag = 1; break; } } if (flag) { ans = mid; L = mid + 1; } else R = mid - 1; } printf("%d\n", ans); } return 0; }