【CodeForces】961 F. k-substrings 字符串哈希+二分

【题目】F. k-substrings

【题意】给定长度为n的串S,对于S的每个k-子串$s_ks_{k+1}...s_{n-k+1},k\in[1,\left \lceil \frac{n}{2} \right \rceil]$,找到满足[奇数长度][严格子串][同时是前缀和后缀]的最长子串。n<=10^6。

【算法】字符串哈希+二分

【题解】任意两个对应子串,它们有一个不变量——它们的中心一定是i和n-i+1。而且固定中心之后,能延伸的最长相等子串是可以二分+哈希得到的。

所以枚举k,二分+哈希处理出以k为中心和对应串相等的最长子串半长L。

然后实际上是一个递减序列覆盖求单点最值的问题,有一个巧妙的解决方法,在k-L+1处标记答案,然后从前往后扫描每次和ans[i]=max{ans[i],ans[i-1]-2}。因为这是一个从大到小的递减序列,所以就不需要考虑终止,因为<0就自然没有意义了。

复杂度O(n log n)。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=1000010;
int ans[maxn],n;
char s[maxn];
int a[maxn],b[maxn],c[maxn],d[maxn];
const int MOD1=993258975,MOD2=934384734,base1=233,base2=197;
bool check(int l,int r,int L,int R){
    int x=r-l+1;//
    int ans1=(a[r]-1ll*a[l-1]*c[x]%MOD1+MOD1)%MOD1;
    int ans2=(a[R]-1ll*a[L-1]*c[x]%MOD1+MOD1)%MOD1;
    if(ans1!=ans2)return 0;
    ans1=(b[r]-1ll*b[l-1]*d[x]%MOD2+MOD2)%MOD2;
    ans2=(b[R]-1ll*b[L-1]*d[x]%MOD2+MOD2)%MOD2;
    if(ans1!=ans2)return 0;
    return 1;
}
int main(){
    scanf("%d%s",&n,s+1);c[0]=d[0]=1;
    for(int i=1;i<=n;i++)a[i]=(1ll*a[i-1]*base1+s[i])%MOD1,b[i]=(1ll*b[i-1]*base2+s[i])%MOD2;
    for(int i=1;i<=n;i++)c[i]=1ll*c[i-1]*base1%MOD1,d[i]=1ll*d[i-1]*base2%MOD2;
    memset(ans,-1,sizeof(ans));
    for(int i=1;i<=n/2;i++){
        int l=1,r=i+1,mid;
        while(l<r){
            mid=(l+r)>>1;
            if(check(i-mid+1,i+mid-1,n-i+1-mid+1,n-i+1+mid-1))l=mid+1;else r=mid;
        }
        l--;
        ans[i-l+1]=max(ans[i-l+1],2*l-1);
    }
    for(int i=1;i<=n/2;i++){
        ans[i]=max(ans[i],ans[i-1]-2);
        printf("%d ",ans[i]);
    }
    if(n&1)printf("-1");
    return 0;
}

字符串哈希:将字符串换算成base进制的数字取模接近int的素模数,比较两段字符串时判断a[r]-a[l-1]*base^(r-l+1)是否相等即可。当然需要双哈希。

如果是建哈希表,就建一条链存真实值。(参考插头DP)

顺便提一下朴素KMP处理一次询问的方法:整个字符串跑出fail数组,那么从n一直fail到最小的>0的位置就是最小首尾匹配串了。KMP的fail树十分强大。

原文地址:https://www.cnblogs.com/onioncyc/p/8823892.html

时间: 2024-08-28 21:52:13

【CodeForces】961 F. k-substrings 字符串哈希+二分的相关文章

codeforces gym 101164 K Cutting 字符串hash

题意:给你两个字符串a,b,不区分大小写,将b分成三段,重新拼接,问是否能得到A: 思路:暴力枚举两个断点,然后check的时候需要字符串hash,O(1)复杂度N*N: 题目链接:传送门 #pragma comment(linker, "/STACK:1024000000,1024000000") #include<iostream> #include<cstdio> #include<cmath> #include<string> #

POJ-2774 字符串哈希+二分

题意:给出s,t两个字符串,求最长公共子串的长度 思路:首先二分答案x,预处理出s中长度为x的子串哈希值并排序,在t中枚举起点,二分查找t[i...i+x]的哈希值 二分查找直接用binary_search()函数 复杂度其实是nlog方 1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<iostream> 5 #define LL long long 6 #def

DNA序列 LOJ NOIP模拟赛 D1T1 字符串哈希

字符串哈希 上代码 #include<cstdio> #include<cstring> #include<string> #include<iostream> using namespace std; template<class T> inline void read(T &_a){ bool f=0;int _ch=getchar();_a=0; while(_ch<'0' || _ch>'9'){if(_ch=='-'

HASH 字符串哈希 映射转化

哈希HASH的本质思想类似于映射.离散化. 哈希,通过给不同字符赋不同的值.并且钦定一个进制K和模数,从而实现一个字符串到一个模意义下的K进制数上. 它的主要目的是判重,用于DFS.BFS判重(八数码),字符串判断相等.出现等等. 本篇总结字符串哈希以及一些应用例题. 为什要用字符串哈希? 因为取出一个字符串是O(n)的,比较一遍又是O(n)的,况且要比较两个甚至多个.这就成了n^2级别的了. 那我们比较数字怎么就不用这么麻烦呢?因为数字可以直接比较,(虽然不知道内部是怎么实现的,反正比一位一位

字符串哈希专题

layout: post title: 字符串哈希专题 author: "luowentaoaa" catalog: true tags: mathjax: true - 字符串 传送门 A.POJ - 1200 A - Crazy Search 摘要 哈希进制转换 题意 一个字符串分成长度为N的字串.且不同的字符不会超过NC个.问总共有多少个不同的子串 思路 以nc作为进制,把一个子串化为这个进制下的数,再用哈希判断 #include<cstdio> #include&l

CF1056E Check Transcription 字符串哈希

传送门 暴力枚举\(0\)的长度,如果对应的\(1\)的长度也是一个整数就去check是否合法.check使用字符串哈希. 复杂度看起来是\(O(st)\)的,但是因为\(01\)两个数中数量较多的至少有\(\frac{|s|}{2}\)个,那么最多有\(\frac{2|t|}{|s|}\)个可能的答案,而每一次check是\(O(|s|)\)的,所以总复杂度是\(O(|t|)\)的 #include<bits/stdc++.h> #define ll long long #define PL

UvaLive 6439 Pasti Pas! 字符串哈希

链接:http://vjudge.net/problem/viewProblem.action?id=47586 题意:给一个字符串,可以将从前数第i~j和从后数第i~j字符串看作一个字符,问整段字符串看作一个回文里有多少个字符. 思路:字符串哈希,从前开始哈希也从后开始哈希,遇到哈希值相同就多两个字符,最后处理一下中间的字符即可. 代码: #include <iostream> #include <cstdio> #include <cstring> #include

CodeForces 451D Count Good Substrings

哎,最近都在做图论,没有练DP,现在一遇到DP就不会了= = 因为有合并这个操作,所以只要是首位相同的字符串肯定是能够构成good串的,那么只要统计在奇数位上出现的0,1的个数和偶数位数,随便递推一下就出来了 #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <climits> #include <string> #i

HDU 4821 杭州现场赛:每个片段字符串哈希比较

I - String Time Limit:1000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u Submit Status Practice HDU 4821 Description Given a string S and two integers L and M, we consider a substring of S as "recoverable" if and only if (i) I