传送门:http://www.hsin.hr/coci/archive/2015_2016/

进去之后点底下的那个。顺带说一句,题目既不是一个英文单词,也不是克罗地亚单词,估计只是从回文串的英文单词palindrome取出前5个字母,再反向复制一遍,造了一个回文串单词。

官方题解说的有点短小精悍,总之步骤就是这样子的:先统计一下原始串有几个回文子串。注意这里我用的是哈希+二分求的,manacher不知道可不可以,可能会很麻烦,因为后面的步骤是奇偶分开讨论的,这里仅仅考虑回文子串的长度为奇数的情况,偶数情况同理,只有一个地方有一点点小差别,后面我会标注(这个小差别葬送了我至少5个小时的调试时间!)。

先解释一下,后文的a[]数组就是我存取原串的数组。

那么现在只需求出,对于每个位置,这个位置的字母如果换成另外一个,会减少以及增加几个回文子串。先考虑增加几个。开一个数组inc[100005][26],inc[i][j]表示第i个位置的字母换成字母j会新增几个回文子串。该怎么计算呢?假设当前正在计算以位置i为中心,可以向两边扩展的长度,按照哈希+二分的算法,当你的left == right时(left和right表示的是二分的上界以及下界),此时是能扩展的最大长度(在我的程序里面,这个长度是包括了第i个字符的,也就是说以i为中心的最长回文子串的左边界是i - left + 1,右边界是i + right - 1),再扩展一点点就会遇到字符不匹配的情况,因此我们就让这两个字符匹配,对应了题目要求的只能修改一个字符。这两个字符匹配后,就再哈希+二分一次,看一下能再匹配几个字符,假设包括修改的那个字符,一共多匹配了x个,那么就:

inc[i - left][a[i + left] - 'a'] += x;  //表示把第(i - left)位置的字符改成第(i + left)字符会新增x个回文子串

inc[i + left][a[i - left] - 'a'] += x;  //类似以上

现在就要求对于每个位置,如果该位置替换一个字母会导致减少几个回文子串,开个数组dec[100005]。举个例子,这里有一个回文串:

a b c d e f e d c b a

假设我们把第三位的'c'换掉,随便换成什么,比如说换成g:

a b g d e f e d c b a

那么这就减少了3个以'f'为中心的回文子串。因此对于一个当前位置i,以他为中心的最长扩展长度为left(就是哈希+二分的结果),如果位置j被替换成其它任意一个串:

①,如果i - left + 1 <= j <= i - 1,那么会减少(j - (i - left))个以位置i为中心的回文子串。所以要dec[j] += (j - (i - left)),代表j位置换了字符,会导致多减少了(j - (i - left))个回文子串。

②,如果i + 1 <= j <= i + left - 1,那么会减少((i + left) - j)个以位置i为中心的回文子串。

这里只考虑第①种情况,第②种同理。显然,不可能按照刚刚说的对于每个位置j都加一遍,这样子效率明显应对不过来。所以,对于当前位置i,(i - left)是一个定值,也就是说(j - (i - left))这个值,随着j的增大而增大,更具体的,是对dec数组的某一段加一个等差数列,比如刚刚那个例子,a b g d e f e d c b a, dec[1] += 1, dec[2] += 2, dec[3] += 3。也就是说,现在的任务就是要快速对一个数组(这里是dec[]数组)的某个区间加上一个以1为首项,1为公差的等差数列。类似差分数组,我们先构建两个辅助数组c1[100005], c2[100005]。

对于“dec[1] += 1, dec[2] += 2, dec[3] += 3”这个操作,我们可以先让区间[1, 3]每项各加1,再让区间[2, 3]各加1,再让区间[3, 3]各加1。按照差分数组的思想,应该要这么做:

1    2    3    4    5

c2     +1             -1

c2          +1        -1

c2               +1   -1

然后再对c2数组求一次前缀和,存到dec里,那么就得到了dec[]的所有值。可是这样一来还不如刚刚的直接操作dec数组快,所以还需要借用一下c1数组。观察到c2数组里[1, 3]的位置都加了一次1,而4位置减了3,因此我们构建的c1数组,其前缀和就是c2数组!类比刚刚的思想,应该这么做:

1    2    3    4    5

c1     +1             -1

c1                    -3   +3

对c1数组求前缀和,保存在c2里,再对c2数组求前缀和,保存在dec里,便得出了dec的所有的值,具体看代码里的incc与decc函数。

之前说回文子串如果有偶数长度,有点小特殊,这就特殊在,如果是奇数长度,那么中心字符被换成了另外一个字符,并不会影响以这个中心字符为中心的回文子串数量,而如果是偶数长度,中心字符有两个,比如a b c c b a,中心字符就是两个c,此时替换任意一个中心字符,会影响到以这两个中心字符为中心的回文子串数量,千万注意!最后,哈希的mod如果让他自然溢出unsigned long long,会被卡一个点,我与std用的都是1e9+7。

#include <cstdio>
#include <cstring>
#include <algorithm> const int maxn = 100005;
const long long base = 131, mod = 1000000007; int n, mx, mx_id;
long long inc[maxn][26], dec[maxn], c1[maxn], c2[maxn], ori_ans, delta_ans;
char a[maxn];
long long poww[maxn], hash1[maxn], hash2[maxn]; inline long long get_hash1(int left, int right) {
return ((hash1[right] - hash1[left - 1] * poww[right - left + 1]) % mod + mod) % mod;
}
inline long long get_hash2(int left, int right) {
return ((hash2[left] - hash2[right + 1] * poww[right - left + 1]) % mod + mod) % mod;
}
inline void incc(int left, int right) {
++c1[left];
c1[right + 1] -= (right - left + 2);
c1[right + 2] += (right - left + 1);
}
inline void decc(int left, int right) {
++c1[right + 2];
c1[left] += (right - left + 1);
c1[left + 1] -= (right - left + 2);
} int main(void) {
freopen("palinilap.in", "r", stdin);
freopen("palinilap.out", "w", stdout);
scanf("%s", a + 1);
n = strlen(a + 1);
a[0] = '$'; poww[0] = 1ull;
for (int i = 1; i <= n; ++i) {
poww[i] = poww[i - 1] * base % mod;
}
for (int i = 1; i <= n; ++i) {
hash1[i] = (hash1[i - 1] * base + a[i]) % mod;
}
for (int i = n; i; --i) {
hash2[i] = (hash2[i + 1] * base + a[i]) % mod;
} int left, right, mid;
for (int i = 1; i < n; ++i) {
for (int j = i; j < i + 2; ++j) {
left = 0;
right = std::min(i, n - j + 1);
while (left < right) {
mid = (left + right + 1) >> 1;
if (get_hash1(i - mid + 1, i) == get_hash2(j, j + mid - 1)) {
left = mid;
}
else {
right = mid - 1;
}
}
ori_ans += (long long)left; if (left > 0) {
if (i == j) {
incc(i - left + 1, i - 1);
decc(j + 1, j + left - 1);
}
else {
incc(i - left + 1, i);
decc(j, j + left - 1);
}
} if (i - left <= 0 || j + left > n) {
continue;
}
long long & tem1 = inc[i - left][a[j + left] - 'a'];
long long & tem2 = inc[j + left][a[i - left] - 'a'];
int ori_left = left;
right = std::min(i - left - 1, n - j - left);
left = 0;
while (left < right) {
mid = (left + right + 1) >> 1;
if (get_hash1(i - ori_left - mid, i - ori_left - 1) == get_hash2(j + ori_left + 1, j + ori_left + mid)) {
left = mid;
}
else {
right = mid - 1;
}
}
tem1 += left + 1;
tem2 += left + 1;
}
} for (int i = 1; i <= n; ++i) {
c2[i] = c2[i - 1] + c1[i];
dec[i] = dec[i - 1] + c2[i];
} for (int i = 1; i <= n; ++i) {
for (int j = 0; j < 26; ++j) {
if (j == a[i] - 'a') {
continue;
}
delta_ans = std::max(delta_ans, inc[i][j] - dec[i]);
}
}
++ori_ans;
printf("%I64d\n", ori_ans + delta_ans);
return 0;
}

  

[coci2015-2016 coii] Palinilap【字符串 哈希】的更多相关文章

  1. ELFhash - 优秀的字符串哈希算法

    ELFhash - 优秀的字符串哈希算法 2016年10月29日 22:12:37 阅读数:6440更多 个人分类: 算法杂论算法精讲数据结构 所属专栏: 算法与数据结构   版权声明:本文为博主原创 ...

  2. JSOI 2016 扭动的字符串

    JSOI 2016 扭动的字符串 题面描述 给出两个长度为\(n\)的字符串\(A,B\) \(S(i,j,k)\)表示把\(A\)中的\([i,j]\)和\(B\)中的\([j,k]\)拼接起来的字 ...

  3. HDU 1880 魔咒词典(字符串哈希)

    题目链接 Problem Description 哈利波特在魔法学校的必修课之一就是学习魔咒.据说魔法世界有100000种不同的魔咒,哈利很难全部记住,但是为了对抗强敌,他必须在危急时刻能够调用任何一 ...

  4. 洛谷P3370 【模板】字符串哈希

    P3370 [模板]字符串哈希 143通过 483提交 题目提供者HansBug 标签 难度普及- 提交  讨论  题解 最新讨论 看不出来,这题哪里是哈希了- 题目描述 如题,给定N个字符串(第i个 ...

  5. HDU2594 Simpsons’ Hidden Talents 字符串哈希

    最近在学习字符串的知识,在字符串上我跟大一的时候是没什么区别的,所以恶补了很多基础的算法,今天补了一下字符串哈希,看的是大一新生的课件学的,以前觉得字符串哈希无非就是跟普通的哈希没什么区别,倒也没觉得 ...

  6. LA 6047 Perfect Matching 字符串哈希

    一开始我用的Trie+计数,但是不是计多了就是计少了,后来暴力暴过去的…… 看了别人的代码知道是字符串哈希,但是仍有几个地方不理解: 1.26^500溢出问题 2.没考虑哈希碰撞? 跪求指点! #in ...

  7. AC日记——【模板】字符串哈希 洛谷 3370

    题目描述 如题,给定N个字符串(第i个字符串长度为Mi,字符串内包含数字.大小写字母,大小写敏感),请求出N个字符串中共有多少个不同的字符串. 友情提醒:如果真的想好好练习哈希的话,请自觉,否则请右转 ...

  8. 从Hash Killer I、II、III论字符串哈希

    首先,Hash Killer I.II.III是BZOJ上面三道很经典的字符串哈希破解题.当时关于II,本人还琢磨了好久,但一直不明白为啥别人AC的代码都才0.3kb左右,直到CYG神犇说可以直接随机 ...

  9. 【NOIP模拟】Grid(字符串哈希)

    题目背景 SOURCE:NOIP2016-RZZ-1 T3 题目描述 有一个 2×N 的矩阵,矩阵的每个位置上都是一个英文小写字符. 现在需要从某一个位置开始,每次可以移动到一个没有到过的相邻位置,即 ...

  10. 洛谷 P3370 【模板】字符串哈希

    洛谷 P3370 [模板]字符串哈希 题目描述 如题,给定N个字符串(第i个字符串长度为Mi,字符串内包含数字.大小写字母,大小写敏感),请求出N个字符串中共有多少个不同的字符串. 友情提醒:如果真的 ...

随机推荐

  1. js的单线程与异步

    一. js 是单线程和异步 1. js 是单线程的,js 的宿主环境(浏览器)是多线程的,实现异步. 2.js是单线程语言,浏览器值分配给js一个主线程,用来执行任务(函数),但一次只能执行一个任务, ...

  2. sdut oj 1163 C语言实验——排列 (当初不会递归生成排列,这个题目现在才补上 刘汝佳给出了写法 *【模板】 当然有生成全排列的函数存在 )

    C语言实验——排列 Time Limit: 1000ms   Memory limit: 65536K  有疑问?点这里^_^ 题目描述 有4个互不相同的数字,请按序输出由其中三个不重复数字组成的排列 ...

  3. LoadRunner中两种录制模式的区别

    决定我们成为什么样人的,不是我们的能力,而是我们的选择. ——<哈利-波特与密室> 一.先看看两种模式的设置和录制脚本的区别 设置HTML录制模式: 设置URL录制模式: HTML脚本: ...

  4. Thrift之代码生成器Compiler原理及源码详细解析1

    我的新浪微博:http://weibo.com/freshairbrucewoo. 欢迎大家相互交流,共同提高技术. 又很久没有写博客了,最近忙着研究GlusterFS,本来周末打算写几篇博客的,但是 ...

  5. 使用WebBrowser自动登录阿里妈妈网站

    窗体上放一个WebBrowser,其Url属性设置为http://www.alimama.com/membersvc/member/login.htm,其他属性为默认 再放一个Button,默认 Bu ...

  6. hdu 1028 & hdu 1398 —— 整数划分(生成函数)

    题目:http://acm.hdu.edu.cn/showproblem.php?pid=1028 整数划分,每个数可以用无限次: 所以构造 f(x) = (1+x+x2+x3+...)(1+x2+x ...

  7. 微信小程序内嵌网页能力开放 小程序支持内嵌网页文档说明

    为了方便开发者灵活配置微信小程序,张小龙现在开放了小程序的内嵌网页功能,这是一个非常大的惊喜啊,以后意味着你只要开放一个手机端网站,就可以制作一个小程序了哦.操作方法1.开发者登录微信小程序后台,选择 ...

  8. JavaScript-Tool-导向:jquery.steps-un

    ylbtech-JavaScript-Tool-导向:jquery.steps 1.返回顶部   2.返回顶部   3.返回顶部   4.返回顶部   5.返回顶部 0. http://www.jqu ...

  9. StackOverFlow页面不正常,因为CDN被墙了

    190.93.247.58 cdn.sstatic.net 198.252.206.140 sstatic.net http://stackoverflow.com/

  10. 堆栈(栈stack)的实现和基本用法(二)

    个人网站http://www.ravedonut.com/ 栈的应用: #include <iostream> #include <stack> using namespace ...