链接:https://www.nowcoder.com/questionTerminal/003482c395bd41c68082f6adc545a600?toCommentId=3230733
来源:牛客网
给定一个字符串,你的任务是计算这个字符串中有多少个回文子串。
("回文串”是一个正读和反读都一样的字符串,比如“level”或者“noon”等等就是回文串。)
具有不同开始位置或结束位置的子串,即使是由相同的字符组成,也会被计为是不同的子串。
可用C++,Java,C#实现相关代码逻辑
输入描述:
输入一个字符串S 例如“aabcb”(1 <= |S| <= 50), |S|表示字符串S的长度。
输出描述:
符合条件的字符串有"a","a","aa","b","c","b","bcb" 所以答案:7
示例1
输入
aabcb
输出
7
题目分析:这个题一般人解法就是暴力,把这个大字符串的所有子串都拿出来,然后每个翻转一下,接着统计一下是回文的个数!
比如我一开始就是这么写的:
import java.util.*;
public class Main {
public static void main(String[] args){
Scanner sc= new Scanner(System.in);
String str = sc.next();
int sum=0;
for (int i = 0; i < str.length(); i++)
for (int j = i+1; j <=str.length(); j++)
{
String str1=str.substring(i, j);
if(str1.equals( new StringBuffer(str1).reverse().toString()))
sum++;
}
System.out.println(sum);
}
}
两次for循环,按给的例子aabcb举例,第一遍截取出来第一个a开始的子串,分别是a,aa,aab,aabc,aabcb,然后用反转函数反转一下,再判断是否相等!相等回文数就加一,不相等就不管!第二遍就是从第二个a开始截取,截出来a,ab,abc,abcb~~~~剩下的自己看!
算法分析:需要O(n^2)次运算,中间还要有各种函数调用,反转处理,总之感觉是不是有很大优化空间!
接下来就讲中心开花,两边散叶的算法:可以省略很多处理!
首先我们看这样一个字符串caabaa,很明显加粗的那一部分是一个比较大的回文,那这段回文核心在哪里呢?越靠近中间越核心,只要中间不是回文,两边的再相等也白瞎。
所以我们应该怎么判断呢?先判断b两边的两个是不是相等的,如果相等,就是一个回文了(aba),然后我们可以接着判断两边的两个字符是不是相等的,一看相等,又一个回文(aabaa)
那我们怎么办呢?
首先我们遍历这个字符数组,每次拿出来一个,然后先比较两边么?
不!
我们要先往右看,比如aabcb,第一个a是一个回文,aa也是一个回文,如果只比较两边,那aa就不可能计算进去。
所以先往右看,看看是不是有aa,bbbb,ccc这样的,只要有就是一个回文,而且不重复。
比如:aaaa,我们第一个a拿出来,然后可以拿出来a,aa,aaa,aaaa这4个
第二个a拿出来,然后可以拿出来a,aa,aaa,这三个!有人一看,这不很多重复了吗?
其实不是的,我们再看看拿出来的这几个:aaaa,aaaa,aaaa,aaaa
aaaa,aaaa,aaaa
看黑色部分,哪里重复了!
找完了这种长得一样的接下来,就要左右开工了,看左右两边是不是相等,比如aabcb,我们找到c这里,然后右找,没找到第2个c,接下来就找这个c的两边,如果相等就多一个回文,然后继续两边找,不相等就break掉。
这样就完了么?
这里有个坑!
比如aabcccb(有个c标黑了)
我们找到了标黑的c那里,先往后找了一个c,回文数+1。再往后找,不符合条件了,然后左右开工,找到一个ccc(aabcccb),回文数又加1~~
这里的ccc我们在找aabcccb(有个c标黑了),从标黑的c那找相同的时候,就会已经找到过ccc(aabcccb)了。
所以,我们要在找到相同的c的时候,记录下最后一个c的位置,然后我们再去左右开工!
不超过这个标记的时候,相等也不加到里面,不相等就break;
比如aabcccb,左右开工俩c(aabcccb)相等,不用增加回文数,但是要继续,接着俩b(aabcccb)相等,且后面那个b位置比标记的c(aabcccb)位置数更大,回文加一。
这样就可以计算完所有回文了!代码如下:
import java.util.*;
public class Main {
public static void main(String[] args){
Scanner sc= new Scanner(System.in);
String[] str = sc.next().split("");
int sum=str.length;
for (int i = 0; i < str.length; i++)
{
int j=i+1;
while (j <str.length)//右看
{
if (str[i].equals(str[j]))
{
sum++;
j++;
}
else
break;
}
int m=i-1;
int n=i+1;
while (m>=0 && n<str.length)//左看
{
if (str[m].equals(str[n])&&n>=j)
{
sum++;
m--;
n++;
}
else
break;
}
}
System.out.println(sum);
}
}
开开心心跑一下!o(∩_∩)o 哈哈
神马Σ(っ °Д °;)っ ?只AC了80%
好吧,还有一个坑!
那就是出现abba这种情况的时候,刚刚的算法没考虑里面是偶数个b这种情况,所以~~~
那就在向右看完了,就在原有基础上直接左右开工!直接解决掉中间是偶数个的问题!
再上代码:AC100%
import java.util.*;
public class Main {
public static void main(String[] args){
Scanner sc= new Scanner(System.in);
String[] str = sc.next().split("");
int sum=str.length;
for (int i = 0; i < str.length; i++)
{
int j=i+1;
while (j <str.length)//右看
{
if (str[i].equals(str[j]))
{
sum++;
j++;
}
else
break;
}
int k=i-1;
while (k>=0&&j <str.length)//右看补加
{
if (str[k].equals(str[j]))
{
sum++;
k--;
j++;
}
else
break;
}
int m=i-1;
int n=i+1;
while (m>=0 && n<str.length)//左看
{
if (str[m].equals(str[n])&&n>=j)
{
sum++;
m--;
n++;
}
else
break;
}
}
System.out.println(sum);
}
}
跑的还挺快!