算法题刷题笔记
1、最长回文串
题目:(牛客网)
Catcher 是MCA国的情报员,他工作时发现敌国会用一些对称的密码进行通信,比如像这些ABBA,ABA,A,123321,但是他们有时会在开始或结束时加入一些无关的字符以防止别国破解。比如进行下列变化 ABBA->12ABBA,ABA->ABAKK,123321->51233214 。因为截获的串太长了,而且存在多种可能的情况(abaaab可看作是aba,或baaab的加密形式),Cathcer的工作量实在是太大了,他只能向电脑高手求助,你能帮Catcher找出最长的有效密码串吗?
解法讲解:
(1)暴力解法O(n^3)
两层嵌套循环找出所有连续字符串,其中i从0到len(s),j从i+1到len(s);内部再嵌套一层,判定字符串是否首位相同,直到中心字符或者0字符。
(2)从中心向外扩散O(n^2)
(3)动态规划(n^2)
以上可以参考:https://blog.csdn.net/qq_32354501/article/details/80084325
(4)马拉车算法O(n)
间隔及两边插入无关特殊字符,将字符长度转换为奇数;
其次,从头计算以每个字符为中心的最大回文半径(使用马拉车算法加速)
详细原理(史上最详细(转)):
c++代码:
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
char s[1000];
char s_new[2000];
int p[2000];
int Init()
{
int len = strlen(s);
s_new[0] = '$';
s_new[1] = '#';
int j = 2;
for (int i = 0; i < len; i++)
{
s_new[j++] = s[i];
s_new[j++] = '#';
}
s_new[j] = '\0'; // 别忘了哦
return j; // 返回 s_new 的长度
}
int Manacher()
{
int len = Init(); // 取得新字符串长度并完成向 s_new 的转换
int max_len = -1; // 最长回文长度
int id;
int mx = 0;
for (int i = 1; i < len; i++)
{
if (i < mx)
p[i] = min(p[2 * id - i], mx - i); // 需搞清楚上面那张图含义, mx 和 2*id-i 的含义
else
p[i] = 1;
while (s_new[i - p[i]] == s_new[i + p[i]]) // 不需边界判断,因为左有'$',右有'\0'
p[i]++;
// 我们每走一步 i,都要和 mx 比较,我们希望 mx 尽可能的远,这样才能更有机会执行 if (i < mx)这句代码,从而提高效率
if (mx < i + p[i])
{
id = i;
mx = i + p[i];
}
max_len = max(max_len, p[i] - 1);
}
return max_len;
}
int main()
{
while (printf("请输入字符串:\n"))
{
scanf("%s", s);
printf("最长回文长度为 %d\n\n", Manacher());
}
return 0;
}
https://subetter.com/algorithm/manacher-algorithm.html
(5)牛客网大佬的O(n)解法:
理论支持:每当增加一个新的字母,最大回文串的长度只能增加1或者2,不可能增加更多,并且,新的最大回文串必然要包含这个字母!
证明:如果新增了一个字母,最大回文串的长度增加了3,这是不可能的,例如:abcdefgfedcba,当增加到最后的b或者a时,是不可能增加3个长度的,因为每增加一个字母,前面必然已经存在一个回文子串,且长度比新串小1或者小2.
所以,从头到尾扫描字符串,每增加一个新的字符,判断以这个字符结尾,且长度为maxLen+1或者maxLen+2的子串是否为回文,如果是,更新最大回文子串。代码如下:
def longestPalindrome(s):
if s==s[::-1]:return len(s)
maxLen=0
for i in range(len(s)):
if i-maxLen>=1 and s[i-maxLen-1:i+1]==s[i-maxLen-1:i+1][::-1]:
maxLen+=2
continue
if i-maxLen>=0 and s[i-maxLen:i+1]==s[i-maxLen:i+1][::-1]:
maxLen+=1
return maxLen
while True:
try:
a=input()
if a:
print(longestPalindrome(a))
except:
break
该if条件:i-maxLen>=1为能切片s[i-maxLen-1:i+1]的必要条件
该if条件:i-maxLen>=0为能切片s[i-maxLen:i+1]的必要条件
因此要放在前面,这种办法对于长度小于等于maxlen的,在中途找到的回文串不加考虑。
如:
输入aaafsfbbbb;依次找到的是
a
aa
aaa
bbbb
最终长度为4(当然也可修改代码让其找出中间过程的等长的回文串)
https://www.nowcoder.com/questionTerminal/12e081cd10ee4794a2bd70c7d68f5507