FJUT3703 这还是一道数论题(二分 + hash + manacher)题解
Posted KirinSB
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了FJUT3703 这还是一道数论题(二分 + hash + manacher)题解相关的知识,希望对你有一定的参考价值。
最后来个字符串签个到吧,这题其实并不难,所需的算法比较基础,甚至你们最近还上过课。
为了降低难度,免得所有人爆零。这里给几个提示的关键字 :字符串,回文,二分,哈希. 注意要对奇偶回文分开二分
这样还不会做,说明基础有所欠缺。
给你一个字符串A和一个字符串B,请你求一个满足以下要求的所有字符串中,最长的字符串C的长度:
-
C必须同时是A和B的子串,即A和B中都必须存在一个子区间和C长得一样
-
C必须是一个回文,即正过来读和反过来读都一样
多组数据,请处理到EOF
每组数据包含,两行,每行都是仅由小写字符构成的字符串,代表A和B。
对于30%的数据。
保证|A|,|B|<=1000,且单个文件的字符总数小于10000
对于100%的数据
保证|A|,|B|<=100000,且单个文件的字符总数小于2e6
其中70%的数据答案为奇数哦
因为没有处理掉字符串尾巴上多余的\'\\r\',所以为了防止读到\'\\r\' 推荐使用scanf("%s");
思路:
更新2.0:
旺神nb。发现A了之后就能看所有人代码了
终于找到Hash的O(n)找公共子串的方法了,直接用unordered_map储存所有满足长度的回文子串。unordered_map查询接近常数级,就是O(1),插入反正比map快,所以总体就是O(n)级。
然后就从7000ms瞬间降到1400ms
代码2.0我没有预处理成全奇回文,而是直接分类奇偶二分。显然我们可以优化一下,奇偶遍历存在与否时可以直接两步两步走,因为奇偶的回文中心刚好交错。然后我们假如先二分奇数,得到一个长度R,那么我二分偶数回文半径至少R / 2 + 1,否则没有意义。我们直接用unordered_map储存s串的回文答案,然后O(n)判断p中有没有,如果怕Hash冲突,可以再验算一边是否一样。
然后我发现时限变成4000ms了...7s的代码卡掉了...只能用2.0的代码过...
这道题后缀数组也能做,JQtxdy +
----------------代码1.0分割线(已T)----------------------------
按照旺神的思路来。
我是把串先按照Manacher处理成只有奇数回文,然后找到最大回文串R,显然最终答案只可能是R,R-2,R-4....那么我直接二分这个最终长度。然后用hash找是否存在这个长度的公共子串,但是我只会Hash的O(nlogn + m)写法啊。在T了几发之后发现题目时限又开大了,8000ms用7400ms擦过,旺神nb。
代码2.0:
#include<cmath> #include<set> #include<queue> #include<cstdio> #include<vector> #include<cstring> #include <iostream> #include<algorithm> #include<bits/stdc++.h> using namespace std; typedef long long ll; typedef unsigned long long ull; const int maxn = 200000 + 10; const ull seed = 131; const int INF = 0x3f3f3f3f; const int MOD = 1000000007; char s[maxn], p[maxn], snew1[maxn]; int pos[maxn], num[maxn]; ull hs[maxn], hp[maxn], fac[maxn], q[maxn]; inline ull getHashP(int l, int r){ return hp[r] - (l == 0? 0 : hp[l - 1]) * fac[r - l + 1]; } inline ull getHashS(int l, int r){ return hs[r] - (l == 0? 0 : hs[l - 1]) * fac[r - l + 1]; } int lens, lenp, lenSnew; int init(int len){ int cnt = 0; snew1[cnt] = \'$\'; for(int i = 0; i < len; i++){ snew1[++cnt] = \'#\'; snew1[++cnt] = s[i]; } snew1[++cnt] = \'#\'; snew1[++cnt] = \'\\0\'; return cnt; } int Manacher(){ int cnt = init(lens); lenSnew = cnt; int id = 0, ans = -1; for(int i = 2; i < cnt; i++){ if(pos[id] + id > i){ pos[i] = min(pos[2 * id - i], pos[id] + id - i); } else pos[i] = 1; while(snew1[i - pos[i]] == snew1[i + pos[i]]) pos[i]++; if(id + pos[id] < i + pos[i]) id = i; ans = max(ans,pos[i] - 1); } return ans; //长度 } bool mid(int l, int r, ull aim){ while(l <= r){ int m = (l + r) >> 1; if(q[m] >= aim){ if(q[m] == aim) return true; r = m - 1; } else l = m + 1; } return false; } unordered_map<ull, int> mp; //$#a#a#a#0 bool checkJi(int len){ mp.clear(); for(int i = 2; i + len - 1 < lenSnew; i += 2){ //s if(pos[i] - 1 >= len){ int R = len / 2; int position = i / 2 - 1; //实际位置 mp[getHashS(position - R, position + R)] = 1; } } for(int i = 0; i + len - 1 < lenp; i++){ ull aim = getHashP(i, i + len - 1); if(mp.count(aim)) return true; } return false; } //$#c#a#a#a#a#0 bool checkOu(int len){ mp.clear(); for(int i = 1; i + len - 1 < lenSnew; i += 2){ //s if(pos[i] - 1 >= len){ int R = len / 2; int position = i / 2 - 1; //实际位置 mp[getHashS(position - R + 1, position + R)] = 1; } } for(int i = 0; i + len - 1 < lenp; i++){ ull aim = getHashP(i, i + len - 1); if(mp.count(aim)) return true; } return false; } int main(){ fac[0] = 1; for(int i = 1; i < 100010; i++) fac[i] = fac[i - 1] * seed; while(scanf("%s%s", s, p) != EOF){ lens = lenp = 0; hs[0] = hp[0] = 0; for(int i = 0; s[i] != \'\\0\'; i++){ //hash if(i == 0) hs[i] = s[i]; else hs[i] = hs[i - 1] * seed + s[i]; lens++; } for(int i = 0; p[i] != \'\\0\'; i++){ if(i == 0) hp[i] = p[i]; else hp[i] = hp[i - 1] * seed + p[i]; lenp++; } int R = Manacher(); //马拉车返回s最大长度 int l, r, ans = 0; l = 1, r = R / 2; while(l <= r){ //偶 int m = (l + r) >> 1; if(checkOu(m * 2)){ l = m + 1; ans = m * 2; } else r = m - 1; } l = ans / 2, r = R / 2; while(l <= r){ //奇 int m = (l + r) >> 1; if(checkJi(m * 2 + 1)){ l = m + 1; ans = max(m * 2 + 1, ans); } else r = m - 1; } printf("%d\\n", ans); } return 0; }
代码:
#include<cmath> #include<set> #include<queue> #include<cstdio> #include<vector> #include<cstring> #include <iostream> #include<algorithm> using namespace std; typedef long long ll; typedef unsigned long long ull; const int maxn = 200000 + 10; const ull seed = 131; const int INF = 0x3f3f3f3f; const int MOD = 1000000007; char s[maxn], p[maxn], snew1[maxn], snew2[maxn]; int pos[maxn], num[maxn]; ull hs[maxn], hp[maxn], fac[maxn], q[maxn]; inline ull getHashP(int l, int r){ return hp[r] - (l == 0? 0 : hp[l - 1]) * fac[r - l + 1]; } inline ull getHashS(int l, int r){ return hs[r] - (l == 0? 0 : hs[l - 1]) * fac[r - l + 1]; } int lens, lenp, lenSnew; int init(int len){ int cnt = 0; snew1[cnt] = \'$\'; for(int i = 0; i < len; i++){ snew1[++cnt] = \'#\'; snew1[++cnt] = s[i]; } snew1[++cnt] = \'#\'; snew1[++cnt] = \'\\0\'; return cnt; } int Manacher(){ int cnt = init(lens); lenSnew = cnt; int id = 0, ans = -1; for(int i = 2; i < cnt; i++){ if(pos[id] + id > i){ pos[i] = min(pos[2 * id - i], pos[id] + id - i); } else pos[i] = 1; while(snew1[i - pos[i]] == snew1[i + pos[i]]) pos[i]++; if(id + pos[id] < i + pos[i]) id = i; ans = max(ans,pos[i] - 1); } return ans; //长度 } bool mid(int l, int r, ull aim){ while(l <= r){ int m = (l + r) >> 1; if(q[m] >= aim){ if(q[m] == aim) return true; r = m - 1; } else l = m + 1; } return false; } bool check(int len){ int tol = 0; for(int i = 0; i + len - 1 < lenp; i++){ //p子串 q[tol++] = getHashP(i, i + len - 1); } sort(q, q + tol); for(int i = 2; i < lenSnew; i += 2){ //找s if(pos[i] - 1 >= len){ int R = len / 2; int position = i / 2 - 1; //实际位置 ull aim = getHashS(position - R, position + R); if(mid(0, tol - 1, aim)){ return true; } } } return false; } int main(){ fac[0] = 1; for(int i = 1; i < maxn; i++) fac[i] = fac[i - 1] * seed; while(scanf("%s%s", snew1, snew2) != EOF){ lens = 0, lenp = 0; //改成全奇回文 s[lens++] = \'0\'; for(int i = 0; snew1[i] != \'\\0\'; i++){ s[lens++] = snew1[i]; s[lens++] = \'0\'; } p[lenp++] = \'0\'; for(int i = 0; snew2[i] != \'\\0\'; i++){ p[lenp++] = snew2[i]; p[lenp++] = \'0\'; } hs[0] = hp[0] = 0; for(int i = 0; i < lens; i++){ //hash if(i == 0) hs[i] = s[i]; else hs[i] = hs[i - 1] * seed + s[i]; } for(int i = 0; i < lenp; i++){ if(i == 0) hp[i] = p[i]; else hp[i] = hp[i - 1] * seed + p[i]; } int cnt = lenSnew; int L = 1, R = Manacher(); //马拉车返回最大长度 int l = 1, r = R / 2; //R = 2 * r + 1 int ans = 0; while(l <= r){ int m = (l + r) >> 1; if(check(2 * m + 1)){ ans = 2 * m + 1; l = m + 1; } else{ r = m - 1; } } printf("%d\\n", ans / 2); } return 0; }
以上是关于FJUT3703 这还是一道数论题(二分 + hash + manacher)题解的主要内容,如果未能解决你的问题,请参考以下文章
fjutacm 3700 这是一道数论题 : dijkstra O(mlogn) 二进制分类 O(k) 总复杂度 O(k * m * logn)