【Foreign】回文串 [hash]

回文串

Time Limit: 10 Sec  Memory Limit: 256 MB

Description

  ASDFZ 的机房中不仅有红太阳,还有蓝太阳和原谅色太阳。

  有一天,太阳们来到机房,发现桌上有不知 道哪个蒟蒻放上的问题:
    令 F (A,B) 表示选择一个串 A 的非空前缀 S 和串 B 的非空后缀 T 使得将串 S 和串 T 拼起来之后是回 文串的方案数。
    现在给定两个串 A 和 B,令 Ai 表示串 A 的第 i 长的后缀,Bi 为串 B 的第 i 长的前缀。

  有 Q 组询问,第 i 组询问给定 xi 和 yi,对每组询问求 F (Axi,Byi) 的值。
  太阳们非常强,自然不会把时间花在这种水题上。快来做做这个题呀。

Input

  第一行一个字符串 str,表示数据类型。
  接下来的两行分别表示字符串 A 和 B。
  接下来一行一个正整数 Q,表示询问的个数。
  接下来 Q 行,每行两个正整数 xi 和 yi。

Output

  输出 Q 行,每行一个整数,表示这一组询问的答案。

Sample Input

  B
  newionyzz
  wyxioiwen
  1
  1 1

  

Sample Output

  16

HINT

  

  

Solution

  显然,我们先将B串倒置,然后答案显然与 以某一位开始的回文串个数 有关(后面有说明),记*F表示这个东西。

  那么这个东西,我们显然可以 枚举中间位置x,然后 字符串hash + 二分 得到 以 c 为中间位置最长回文子串的L、R

  那么贡献显然是:[L, x]这一段的 F 每一个+1。这样我们就得到了 F 数组,对于 B 做相同处理,就得到了 G 数组(性质与F一样)。

  考虑怎么统计答案,我们先用字符串hash + 二分得到 以x开始的A以y开始的BLCP

  那么显然有:

  为什么呢?考虑从LCP中取出一位,那么显然形如:a__a,中间可以填的就是以后面某一位开始的F or G

  为什么要加上l呢?因为这就是中间不填的个数。注意不能加上F[x] or G[y],因为形如a__a,不能没有头尾的a。

  这样我们就AC了这道题啦!

  (QAQ 万恶的出题人最后3分是卡自然溢出hash的!)。

Code

  1 #include<iostream>
  2 #include<string>
  3 #include<algorithm>
  4 #include<cstdio>
  5 #include<cstring>
  6 #include<cstdlib>
  7 #include<cmath>
  8 #include<vector>
  9 using namespace std;
 10 typedef long long s64;
 11
 12 const int ONE = 800005;
 13 const int Base = 10007;
 14 const int MOD = 1e9 + 7;
 15
 16 int n, m, Q;
 17 int x, y;
 18 char c[5], A[ONE], B[ONE];
 19 int Pow[ONE];
 20 int pre_A[ONE], pre_B[ONE], suc_A[ONE], suc_B[ONE];
 21 s64 F[ONE], G[ONE];
 22 s64 Sum_F[ONE], Sum_G[ONE];
 23
 24 struct power
 25 {
 26         int l, r;
 27 };
 28
 29 int get()
 30 {
 31         int res=1,Q=1;char c;
 32         while( (c=getchar())<48 || c>57 )
 33         if(c==‘-‘)Q=-1;
 34         res=c-48;
 35         while( (c=getchar())>=48 && c<=57 )
 36         res=res*10+c-48;
 37         return res*Q;
 38 }
 39
 40 void Deal_first()
 41 {
 42         Pow[0] = 1;
 43         for(int i = 1; i <= max(n, m); i++)
 44             Pow[i] = (s64)Pow[i - 1] * Base % MOD;
 45         for(int i = 1; i <= n; i++) pre_A[i] = (s64)pre_A[i - 1] * Base % MOD + A[i];
 46         for(int i = 1; i <= n; i++) suc_A[i] = (s64)suc_A[i - 1] * Base % MOD + A[n - i + 1];
 47         for(int i = 1; i <= m; i++) pre_B[i] = (s64)pre_B[i - 1] * Base % MOD + B[i];
 48         for(int i = 1; i <= m; i++) suc_B[i] = (s64)suc_B[i - 1] * Base % MOD + B[m - i + 1];
 49 }
 50
 51 int Pre(int PD, int l, int r)
 52 {
 53         if(PD == 1) return (pre_A[r] - (s64)pre_A[l - 1] * Pow[r - l + 1] % MOD + MOD) % MOD;
 54         if(PD == 2) return (pre_B[r] - (s64)pre_B[l - 1] * Pow[r - l + 1] % MOD + MOD) % MOD;
 55 }
 56
 57 int Suc(int PD, int L, int R)
 58 {
 59         int Limit = PD == 1 ? n : m;
 60         int l = Limit - R + 1, r = Limit - L + 1;
 61         if(PD == 1) return (suc_A[r] - (s64)suc_A[l - 1] * Pow[r - l + 1] % MOD + MOD) % MOD;
 62         if(PD == 2) return (suc_B[r] - (s64)suc_B[l - 1] * Pow[r - l + 1] % MOD + MOD) % MOD;
 63 }
 64
 65 int Check(int PD, int l, int r)
 66 {
 67         int Limit = PD == 1 ? n : m;
 68         if(l < 1 || r > Limit) return 0;
 69         return Pre(PD, l, r) == Suc(PD, l, r);
 70 }
 71
 72 power Find(int PD, int PosA, int PosB)
 73 {
 74         int l = 1, r = PD == 1 ? n : m;
 75         while(l < r - 1)
 76         {
 77             int mid = l + r >> 1;
 78             int Left = PosA - mid + 1, Right = PosB + mid - 1;
 79             if(Check(PD, Left, Right)) l = mid;
 80             else r = mid;
 81         }
 82
 83         int Left, Right;
 84         Left = PosA - r + 1, Right = PosB + r - 1;
 85         if(Check(PD, Left, Right)) return (power) {Left, Right};
 86
 87         Left = PosA - l + 1, Right = PosB + l - 1;
 88         return (power) {Left, Right};
 89 }
 90
 91 void Deal_F()
 92 {
 93         for(int i = 1; i <= n; i++)
 94         {
 95             power pos;
 96             pos = Find(1, i, i);
 97             F[pos.l]++, F[i + 1]--;
 98             if(A[i] != A[i + 1]) continue;
 99
100             pos = Find(1, i, i + 1);
101             F[pos.l]++, F[i + 1]--;
102         }
103         for(int i = 1; i <= n + 1; i++) F[i] += F[i - 1];
104         for(int i = 1; i <= n + 1; i++) Sum_F[i] = Sum_F[i - 1] + F[i];
105 }
106
107 void Deal_G()
108 {
109         for(int i = 1; i <= m; i++)
110         {
111             power pos;
112             pos = Find(2, i, i);
113             G[pos.l]++, G[i + 1]--;
114             if(B[i] != B[i + 1]) continue;
115
116             pos = Find(2, i, i + 1);
117             G[pos.l]++, G[i + 1]--;
118         }
119         for(int i = 1; i <= m + 1; i++) G[i] += G[i - 1];
120         for(int i = 1; i <= m + 1; i++) Sum_G[i] = Sum_G[i - 1] + G[i];
121 }
122
123 int Get_LCP(int L1, int L2)
124 {
125         int l = 0, r = min(n - L1 + 1, m - L2 + 1);
126
127         while(l < r - 1)
128         {
129             int mid = l + r >> 1;
130
131             if(Pre(1, L1, L1 + mid - 1) == Pre(2, L2, L2 + mid - 1)) l = mid;
132             else r = mid;
133         }
134
135         if(Pre(1, L1, L1 + r - 1) == Pre(2, L2, L2 + r - 1)) return r;
136         return l;
137 }
138
139 int main()
140 {
141         scanf("%s", c + 1);
142         scanf("%s", A + 1);    n = strlen(A + 1);
143         scanf("%s", B + 1); m = strlen(B + 1);
144         for(int i = 1, j = m; i <= j; i++, j--) swap(B[i], B[j]);
145         Deal_first();
146         Deal_F();    Deal_G();
147
148         Q = get();
149         while(Q--)
150         {
151             x = get(), y = get();
152             int l = Get_LCP(x, y);
153             s64 Ans = Sum_F[x + l] - Sum_F[x] + Sum_G[y + l] - Sum_G[y] + l
154             printf("%lld\n", Ans);
155         }
156 }

时间: 2024-11-05 12:29:18

【Foreign】回文串 [hash]的相关文章

BZOJ 2565 最长双回文串 Hash+二分

题目大意:给定一个字符串,求一个最长的子串,该字串可以分解为两个回文子串 傻逼的我又忘了Manacher怎么写了= = 无奈Hash+二分吧 首先将字符串用分隔符倍增,然后求出以每个点为中心的最长回文半径 然后考虑两个回文串怎么合并成一个 我们发现图中以i为中心的回文串和以j为中心的回文串合并后长度恰好为(j-i)*2 能合并的前提是以两个点为中心的回文串有交点 那么对于每个j我们要求出有交点的最左侧的i 维护一个后缀min随便搞搞就可以了 #include <cstdio> #include

URAL 1989 Subpalindromes(回文串 线段树 多项式hash)

1989. Subpalindromes Time limit: 0.5 second Memory limit: 64 MB You have a string and queries of two types: replace i'th character of the string by character a; check if substring sj...sk is a palindrome. Input The first line contains a string consis

bzoj 2124 等差子序列 树状数组维护hash+回文串

等差子序列 Time Limit: 3 Sec  Memory Limit: 259 MBSubmit: 1919  Solved: 713[Submit][Status][Discuss] Description 给一个1到N的排列{Ai},询问是否存在1<=p1<p2<p3<p4<p5<…<pLen<=N (Len>=3), 使得Ap1,Ap2,Ap3,…ApLen是一个等差序列. Input 输入的第一行包含一个整数T,表示组数. 下接T组数据,

[BZOJ]4755: [Jsoi2016]扭动的回文串

Time Limit: 10 Sec  Memory Limit: 512 MB Description JYY有两个长度均为N的字符串A和B. 一个"扭动字符串S(i,j,k)由A中的第i个字符到第j个字符组成的子串与B中的第j个字符到第k个字符组成的子串拼接而成. 比如,若A='XYZ',B='UVW',则扭动字符串S(1,2,3)='XYVW'. JYY定义一个"扭动的回文串"为如下情况中的一个: 1.A中的一个回文串: 2.B中的一个回文串: 3.或者某一个回文的扭动

回文串问题总结

回文串的问题很经典,也很常见,涉及到递归,循环,动态规划等方面,这里总结一下几种类型,供以后回顾,有问题请大家指正 1.回文串的判断   leetcode上的题目 bool isPalindrome(const char* src) { if(src == NULL)return true; int end = strlen(src)-1,begin = 0; while(begin < end)//从两边向中间判断,当然也可以从中间向两边判断 { if(src[begin] != src[en

P4324 [JSOI2016]扭动的回文串

传送门 对\(A\).\(B\)串各跑一遍\(manacher\),求出第\(1\).\(2\)类扭动回文串的最大长度. 考虑第三类的扭动回文串\(S(i,j,k)\),一定可以表示为\(A(i,l)+A(l+1,j)+B(j,k)\)或\(A(i,j)+B(j,l)+B(l+1,k)\),其中,第一段与第三段对称(第一段正着\(Hash\)和第三段反着\(Hash\)相同),第二段是一个回文子串,三段都可以是空串. 我们可以分别在\(AB\)上枚举对称中心,然后感性理解一下发现肯定是取以该对称

HDU 6599 I Love Palindrome String (回文树+hash)

题意 找如下子串的个数: (l,r)是回文串,并且(l,(l+r)/2)也是回文串 思路 本来写了个回文树+dfs+hash,由于用了map所以T了 后来发现既然该子串和该子串的前半部分都是回文串,所以该子串的前半部分和后半部分是本质相同的! 于是这个log就去掉了 代码 #include<iostream> #include<cstdio> #include<algorithm> #include<cmath> #include<cstring>

[CSP-S模拟测试]:回文(hash+二维前缀和)

题目描述 闲着无聊的$YGH$秒掉上面两道题之后,开始思考有趣的回文串问题了. 他面前就有一个漂浮着的字符串.显然$YGH$是会$manacher$的,于是他随手求出了这个字符串的回文子串个数.但是他不满足于这个问题,他打算搞出一个数据结构,能够快速求出这个字符串下标为$[l,r]$的子串的回文子串个数(相同的回文子串需重复计数).但是这实在是太简单啦,他打算考考辣鸡$YYR$,可是辣鸡至极的$YYR$完全没有思路. 于是,$YGH$扬长而去,在衣袖带起的一小片尘土之中,沉思的$YYR$依旧在那

最少回文串--牛客网(秋招备战专场三模)-C++方向

题目描述:一个字符串从左向右和从右向左读都完全一样则是回文串,给定一个字符串,问该字符串中的字符所能组成的最少的回文串的个数为多少 解题思路:如果一个字符出现的次数为偶数,则必能组成回文串,如果一个字符出现奇数次,只能自己组成回文串,题目中问最少的回文串数目,即求出现次数为奇数次的字符个数即可,定义a存储每个字符出现的次数,统计出现奇数次的字符的个数,即为输出 1 #include <iostream> 2 #include <string> 3 using namespace s