1. 程式人生 > >FJUT3703 這還是一道數論題(二分 + hash + manacher)題解

FJUT3703 這還是一道數論題(二分 + hash + manacher)題解

鏈接 都是 pan position fault tac adding 求一個 位置

Problem Description

最後來個字符串簽個到吧,這題其實並不難,所需的算法比較基礎,甚至你們最近還上過課。

為了降低難度,免得所有人爆零。這裏給幾個提示的關鍵字 :字符串,回文,二分,哈希. 註意要對奇偶回文分開二分

這樣還不會做,說明基礎有所欠缺。

給你一個字符串A和一個字符串B,請你求一個滿足以下要求的所有字符串中,最長的字符串C的長度:

  1. C必須同時是A和B的子串,即A和B中都必須存在一個子區間和C長得一樣

  2. C必須是一個回文,即正過來讀和反過來讀都一樣

Input

多組數據,請處理到EOF

每組數據包含,兩行,每行都是僅由小寫字符構成的字符串,代表A和B。

對於30%的數據。

保證|A|,|B|<=1000,且單個文件的字符總數小於10000

對於100%的數據

保證|A|,|B|<=100000,且單個文件的字符總數小於2e6

其中70%的數據答案為奇數哦

因為沒有處理掉字符串尾巴上多余的‘\r‘,所以為了防止讀到‘\r‘ 推薦使用scanf("%s");

鏈接

思路:

技術分享圖片

按照旺神的思路來。

我是把串先按照Manacher處理成只有奇數回文,然後找到最大回文串R,顯然最終答案只可能是R,R-2,R-4....那麽我直接二分這個最終長度。然後用hash找是否存在這個長度的公共子串,但是我只會Hash的O(nlogn + m)寫法啊。在T了幾發之後發現題目時限又開大了,8000ms用7400ms擦過,旺神nb。

代碼:

#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)題解