Ball Blasting Game

Morteza is playing a ball blasting game. In this game there is a chain of different colored balls. He is expected to explode as many balls as possible by aligning same-colored balls and making a sequence of them. To align balls, he can aim and shoot a new ball into a position in the chain, thus adding it there. If the new ball makes a sequence of two or more same-colored balls with its immediate neighbors, then the sequence blows up breaking the chain up into two parts. The two sections draw together to reform a single chain. Again, if the colors of the newly aligned balls (on joining ends of the two sections) match, the entire run of the suit explodes. New explosions ensue as long as joining sections bear new matches.

For instance, consider a chain symbolized in the string "GGGBWWRRWRR", with each letter representing the color of the corresponding ball in the chain. The train of balls therefore is composed of 6 sequences in 4 colors. Here, a ball can be added in 12 different positions. Shooting a red ball between the two middle red balls triggers two successive explosions which leave the chain "GGGBRR".

Morteza reaches a challenge stage in which he has only one ball to shoot but the color of which he can choose. He may not advance to the next stage unless he takes a shot that sets off the largest possible number of explosions. He has to replay all through this repetitive stage should he fail and needs your help in finding the largest possible number of successive explosions to save him a great deal of suffering.

Input

The first line of input contains an integer T≤100 denoting the number of test-cases. Each test-case is represented by a single line containing a string of upper case letters ('A'-'Z') of size of at most 100,000.

Output

For each test-case, output on a single line the maximum possible number of explosions.

Sample Input

Sample Output

3

GGGBWWRRWRR

XAABCBAXAAAA

CCC

2

4

1

【题目大意】

给定一个字符串,向某个位置插入一个字符C使得两个或多个字符C相邻,然后消除这些字符,消除后得到左右两个新字符串,连接两个新字符串,如果连接处是两个或多个相同的字符,则同样消除掉,以此类推

【题解】

把给定的字符串进行预处理,相邻多个相同的字符合并成一个字符

如GGGBWWRRWRR合并成GBWRWR

XAABCBAXAAA合并成XABCBAXA

然后就转化成求最长回文子串问题,

那么怎么求最长回文子串?o(n^2)的算法显然不行,那么需要小于n^2的算法

百度得到Manacher算法

这篇博文讲的不错

http://blog.163.com/zhaohai_1988/blog/static/2095100852012716105847112/

本题代码

#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#define LEN 1000000
using namespace std;
int len,p[LEN],ans;
char s[LEN],GivenStr[LEN]; void init()
{
memset(s,,sizeof(s));
memset(p,,sizeof(p));
s[]='$';
s[]='#';
len=;
ans=; getchar();
scanf("%s",GivenStr);
int lenGiven=strlen(GivenStr);
for (int i=; i<lenGiven; i++)
{
if (GivenStr[i]==GivenStr[i+]) continue;
s[++len]=GivenStr[i];
s[++len]='#';
}
} void LPS()
{
int id=,mx=;
for (int i=; i<len; i++)
{
if (mx>i)
p[i]=min(p[*id-i],mx-i);
else
p[i]=; while (s[i+p[i]]==s[i-p[i]]) p[i]++; if (p[i]+i>mx)
{
mx=p[i]+i;
id=i;
}
ans=max(ans,p[i]);
}
} void out()
{
printf("%d\n",ans/);
} int main()
{
int T;
scanf("%d",&T);
while (T--)
{
init();
LPS();//Longest Palindromic Substring
out();
}
}

=================

转载注明出处

这个算法有一个很巧妙的地方,它把奇数的回文串和偶数的回文串统一起来考虑了。这一点一直是在做回文串问题中时比较烦的地方。这个算法还有一个很好的地方就是充分利用了字符匹配的特殊性,避免了大量不必要的重复匹配。

算法大致过程是这样。先在每两个相邻字符中间插入一个分隔符,当然这个分隔符要在原串中没有出现过。一般可以用‘#’分隔。这样就非常巧妙的将奇数长度回文串与偶数长度回文串统一起来考虑了(见下面的一个例子,回文串长度全为奇数了),然后用一个辅助数组P记录以每个字符为中心的最长回文串的信息。P[id]记录的是以字符str[id]为中心的最长回文串,当以str[id]为第一个字符,这个最长回文串向右延伸了P[id]个字符。
    原串:       w     a    a     b     w    s   w      f     d
    新串:     #  w  # a # a #  b  # w # s # w #  f  # d #
辅助数组P: 1  2   1 2 3 2 1   2  1  2 1 4 1  2 1  2  1 2 1

这里有一个很好的性质,P[id]-1就是该回文子串在原串中的长度(包括‘#’)。如果这里不是特别清楚,可以自己拿出纸来画一画,自己体会体会。当然这里可能每个人写法不尽相同,不过我想大致思路应该是一样的吧。

现在的关键问题就在于怎么在O(n)时间复杂度内求出P数组了。只要把这个P数组求出来,最长回文子串就可以直接扫一遍得出来了。

那么怎么计算P[i]呢?该算法增加两个辅助变量(其实一个就够了,两个更清晰)id和mx,其中id表示最大回文子串中心的位置,mx则为id+P[id],也就是最大回文子串的边界。

然后可以得到一个非常神奇的结论,这个算法的关键点就在这里了:如果mx > i,那么

P[i] >= MIN(P[2 * id - i], mx - i)。就是这个串卡了我非常久。实际上如果把它写得复杂一点,理解起来会简单很多:

//记j = 2 * id - i,也就是说 j 是 i 关于 id 的对称点。
if (mx - i > P[j])
P[i] = P[j];
else /* P[j] >= mx - i */
P[i] = mx - i; // P[i] >= mx - i,取最小值,之后再匹配更新。

当 mx - i > P[j] 的时候,以S[j]为中心的回文子串包含在以S[id]为中心的回文子串中,由于 i 和 j 对称,以S[i]为中心的回文子串必然包含在以S[id]为中心的回文子串中,所以必有 P[i] = P[j]。

#include<vector>
#include<iostream>
using namespace std; const int N=;
int n, p[N];
char s[N], str[N]; #define _min(x, y) ((x)<(y)?(x):(y)) void kp()
{
int i;
int mx = ;
int id;
for(i=n; str[i]!=; i++)//清除n后边多余的部分
str[i] = ; //没有这一句有问题。。就过不了ural1297,比如数据:ababa aba
for(i=; i<n; i++)
{
if( mx > i )
p[i] = _min( p[*id-i], p[id]+id-i );
//因为是从左往右扫描的这里i>id, 2*id-i是i关于id的对称点,该对称点在id的左端
//p[id]+id是描述中的mx,即id向右延伸的端点位置
//显然向右延伸是可能超出mx的,所以要有下边的for循环
else
p[i] = ;
for(; str[i+p[i]] == str[i-p[i]]; p[i]++); if( p[i] + i > mx )//更新mx与id,因为mx是向右延伸的最大长度,所以实时更新
{
mx = p[i] + i;
id = i;
}
}
} void init()//处理字符串
{
int i, j, k;
str[] = '$';
str[] = '#';
for(i=; i<n; i++)
{
str[i*+] = s[i];
str[i*+] = '#';
}
n = n*+;
s[n] = ;
} int main()
{
int i, ans;
while(scanf("%s", s)!=EOF)
{
n = strlen(s);//n为给定字符串s的长度
init();
kp();
ans = ;
for(i=; i<n; i++)
if(p[i]>ans)//寻找最大的长度ans
ans = p[i];
printf("%d\n", ans-);
}
return ;
}

当 P[j] > mx - i 的时候,以S[j]为中心的回文子串不完全包含于以S[id]为中心的回文子串中,但是基于对称性可知,也就是说以S[i]为中心的回文子串,其向右至少会扩张到mx的位置,也就是说 P[i] >= mx - i。至于mx之后的部分是否对称,就只能老老实实去匹配了。

由于这个算法是线性从前往后扫的。那么当我们准备求P[i]的时候,i以前的P[j]我们是已经得到了的。我们用mx记在i之前的回文串中,延伸至最右端的位置。同时用id这个变量记下取得这个最优mx时的id值。(注:为了防止字符比较的时候越界,我在这个加了‘#’的字符串之前还加了另一个特殊字符‘$’,故我的新串下标是从1开始的)

if( mx > i)

p[i]=MIN( p[2*id-i], mx-i);

就是当前面比较的最远长度mx>i的时候,P[i]有一个最小值。这个算法的核心思想就在这里,为什么P数组满足这样一个性质呢?

(下面的部分为图片形式)

LEETCODE上也有这个题的详细说明,不过是英文版本的。

UVA 12378 Ball Blasting Game 【Manacher回文串】的更多相关文章

  1. UVA 11584 Partitioning by Palindromes 划分回文串 (Manacher算法)

    d[i]表示前面i个字符划分成的最小回文串个数, 转移:当第i字符加进来和前面区间j构成回文串,那么d[i] = d[j]+1. 要判断前面的字符j+1到i是不是回文串,可以用Manacher算法预处 ...

  2. 2015 UESTC Training for Search Algorithm & String - M - Palindromic String【Manacher回文串】

    O(n)的复杂度求回文串:Manacher算法 定义一个回文值,字符串S是K重回文串,当且仅当S是回文串,且其长度为⌊N/2⌋的前缀和长度为⌊N/2⌋的后缀是K−1重回文串 现在给一个2*10^6长度 ...

  3. HDU3068 最长回文 MANACHER+回文串

    原题链接:http://acm.hdu.edu.cn/showproblem.php?pid=3068 Problem Description 给出一个只由小写英文字符a,b,c...y,z组成的字符 ...

  4. Best Reward---hdu3613(manacher 回文串)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3613 题意就是给你一个串s 然后求把s分成两部分之后的价值总和是多少,分开的串 如果是回文那么价值就是 ...

  5. Manacher回文串算法学习记录

    FROM:  http://hi.baidu.com/chenwenwen0210/item/482c84396476f0e02f8ec230 #include<stdio.h> #inc ...

  6. 【模板】Manacher 回文串

    推荐两个讲得很好的博客: http://blog.sina.com.cn/s/blog_70811e1a01014esn.html https://segmentfault.com/a/1190000 ...

  7. BZOJ 2342 回文串-Manacher

    题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=2342 思路:先跑一遍Manacher求出p[i]为每个位置为中心的回文半径,因为双倍回文串 ...

  8. BZOJ 2565 回文串-Manacher

    题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=2565 题意:中文题 思路:定义L[i],R[i].表示以i为左端点/右端点时,最长回文串长 ...

  9. POJ 3974 回文串-Manacher

    题目链接:http://poj.org/problem?id=3974 题意:求出给定字符串的最长回文串长度. 思路:裸的Manacher模板题. #include<iostream> # ...

随机推荐

  1. Android中两种设置全屏或者无标题的方法

    在开发中我们经常需要把我们的应用设置为全屏或者不想要title, 这里是有两种方法的,一种是在代码中设置,另一种方法是在配置文件里改: 一.在代码中设置: package jason.tutor; i ...

  2. QQ情侣头像~

                       

  3. 学会用Clang来进行内存泄露分析

    最近项目出现了内存泄露的问题,对于PC x86平台来说,一点点的内存泄露往往不会出错,很难进行debug调试.这个时候我们可以用到苹果给我们带来的神器--Clang编译器来进行内存泄露分析检测,开关打 ...

  4. jQuery 使用 jQuery UI 部件工厂编写带状态的插件(翻译)

    首先,我们要创建一个progress bar,它只允许我们简单的设置进度值.正如我们接下来将要看到的,我们需要通过调用 jQuery.widget 及其两个参数来实现这一操作,这两个参数分别是:将要创 ...

  5. javascript权威指南学习笔记1

    打开这本书,进入到javascript的世界.以前都是看各种视频,感觉什么收获也没有,反而弄得脑袋混乱,希望能够按照这本书的节奏掌握javascript这门语言,为我的前端学习打下基础. 学习前准备: ...

  6. python学习第二课要点记录

    字典使用时,使用for k,v in items():要将字典转换为元组,因此效率较低,如果数据量较大,就不建议使用这样的形式获取key和value的值,而要使用 for item in dict: ...

  7. Html Meta 标签详解

    http://www.dreamdu.com/xhtml/tag_meta/

  8. 【Maven实战】仓库介绍和Nexus的安装

    在Maven中我们之前进行配置一个项目的依赖时,引用一下jar包,这些jar包第一次都会从一个地方进行下载,这个地方称为仓库,而对于仓库一般有本地仓库和中心仓库之分,但是我们一般在做项目时会在自己的服 ...

  9. Milking Grid

    poj2185:http://poj.org/problem?id=2185 题意:在一个字符矩阵中,找一个最小的字符子矩阵,使其能够覆盖整个矩阵. 题解:在KMP中i-next[i]是这能够覆盖这个 ...

  10. hdu 3400 Line belt

    题意:给你两条线段AB,CD:然后给你在AB,CD上的速度P,Q,在其它部分的速度是R,然后求A到D的最短时间. 思路:用三分枚举从AB线段上离开的点,然后再用三分枚举在CD的上的点找到最优点,求距离 ...