字符处理编程笔记一

字符处理编程笔记一

1.最长回文子串

题目
给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。

示例 1:

输入: "babad"
输出: "bab"
注意: "aba" 也是一个有效答案。

示例 2:

输入: "cbbd"
输出: "bb"

解法一:暴力解法
1.两重循环遍历所有子串;
2.判断回文子串。

时间复杂度: O ( n 3 ) O(n^3) O(n3)

#include <iostream>
#include <vector>
#include <string>

using namespace std;

int main() {

	string str;

	getline(cin, str);

	int max = 0;
	int start = 0;

	for (int i = 0; i < str.size(); i++)
	{
		for (int j = i; j < str.size(); j++)
		{
			int flag = 1;
			for (int k = 0; k < (j - i + 1) / 2; k++)
			{
				if (str[k + i] != str[j - k])
				{
					flag = 0;
					break;
				}
			}

			if (flag && j - i + 1 > max)
			{
				max = j - i + 1;
				start = i;
			}
		}
	}
	cout << str.substr(start, max) << endl;
	
	return 0;
}

解法二:中心扩展法
1.遍历一遍字符串,将字符作为子串中心;
2.从子串中心从左右同时遍历,判断是否相同。
(奇子串n个和偶子串n-1个,二者区别在于需考虑是否用中心的字符)
时间复杂度: O ( n 2 ) O(n^2) O(n2)

#include <iostream>
#include <vector>
#include <string>
#include <algorithm>

using namespace std;

int expand(string s, int left, int right)
{
	int l = left;
	int r = right;
	while (l >= 0 && r < s.size()&&s[l] && s[r] && s[l] == s[r])
	{
		l--;
		r++;
	}
	return r - l - 1;
}

string findLongestPalindrome(string s)
{
	if (s.empty() || s.size() < 1)
		return s;
	int start = 0;
	int end = 0;
	int len = 0;
	for (int i = 0; i < s.size(); i++)
	{
		int len1 = expand(s, i, i);
		int len2 = expand(s, i, i + 1);
		len = max(len1, len2);
		if (len > end - start)
		{
			start = i - (len - 1) / 2;
			end = i + len / 2;
		}
	}
	return s.substr(start, end - start + 1);
}

int main() {

	string str;

	getline(cin, str);

	//中心扩展法
	cout << findLongestPalindrome(str) << endl;
	return 0;
}

解法三:动态规划
状态转移方程:
d p [ j ] [ i ] = { t r u e , j = i ; s t r [ j ] = = s t r [ i ] , j − i < 2 s t r [ j ] = = s t r [ i ] & & d p [ j + 1 ] [ i − 1 ] , j − 1 > = 2 dp[j][i]=\left\{ \begin{aligned} &true ,&j=i;\\ &str[j]==str[i] ,&j-i < 2\\ &str[j]==str[i]\&\&dp[j+1][i-1],&j-1 >=2 \end{aligned} \right. dp[j][i]=true,str[j]==str[i],str[j]==str[i]&&dp[j+1][i1],j=i;ji<2j1>=2
时间复杂度: O ( n 2 ) O(n^2) O(n2)

string longestPalindrome(string s) {
		const int n=s.size();
		bool dp[n][n];
		fill_n(&dp[0][0],n*n,false);
		int max_len=1; //保存最长回文子串长度
		int start=0;//保存最长回文子串起点
		for(int i=0;i<s.size();++i)
		{
			for(int j=0;j<=i;++j)
			{
				if(i-j<2)
					dp[j][i]=(s[i]==s[j]);
				else
					dp[j][i]=(s[i]==s[j] && dp[j+1][i-1]);
				if(dp[j][i] && max_len<(i-j+1))
				{
					max_len=i-j+1;
					start=j;
				}
			}
		}
		return s.substr(start,max_len);
	}

解法四:马拉车算法(Manacher)
1.扩展字符串均为奇回文,避免奇回文和偶回文两次处理;
2.建立数组,存储以str[i]为中心的最长回文半径;
3.确定最长回文半径和最长回文子串长度之间的关系: m a x L e n = p [ i ] − 1 maxLen = p[i] - 1 maxLen=p[i]1;
4.首位添加两个特殊符号,最长回文子串的起始索引 i n d e x = ( i − p [ i ] ) ÷ 2 index = (i - p[i])\div 2 index=(ip[i])÷2

5.设置两个辅助变量:
m x : mx: mx:表示当前能延伸到最右端的回文子串的最右端点;
i d : id: id:表示当前能延伸到最右端的回文子串的中心端点。
p [ i ] = m x > i ? m i n ( p [ 2 × i d − i ] , m x − i ) : 1 p[i] = mx > i ? min(p[2 \times id - i], mx - i) : 1 p[i]=mx>i?min(p[2×idi],mxi):1
由于回文子串中心对称,当 p [ i ] + i < m x p[i]+i <mx p[i]+i<mx时, p [ i ] = = p [ 2 × i d − i ] p[i] ==p[2 \times id - i] p[i]==p[2×idi],否则, p [ i ] = = m x − i p[i] == mx - i p[i]==mxi,而超过mx的部分需要继续遍历得到。
时间复杂度: O ( n ) O(n) O(n)

string rebuildStr(string &s)
{
	string newStr(s.length() * 2 + 2,' ');
	int k = 0;
	newStr[k++] = '@';
	for (int i = 0; i < s.length(); i++)
	{
		newStr[k++] = '#';
		newStr[k++] = s[i];
	}
	newStr[k++] = '#';
	newStr[k] = '*';

	return newStr;
}

int manacher(string &s)
{
	string str = rebuildStr(s);
	int len = str.size();
	vector<int> p(len, 0);
	int mx = 0,id = 0;//mx为最右边,id为中心点

	//最长回文子串的长度和索引
	int maxLen = 0;
	int index = 0;


	for (int i = 1; i < str.size(); i++) {
		p[i] = mx > i ? min(p[2 * id - i], mx - i) : 1;

		while (str[i + p[i]] == str[i - p[i]]) p[i]++;//向左右扩展
		
	    //更新mx
		if (p[i] + i > mx) {
			mx = p[i] + i;
			id = i;
		}

		if (maxLen < p[i] - 1)
		{
			maxLen = p[i] - 1;
			index = i;
		}
	}

	int start = (index - maxLen) / 2;
	string subStr = s.substr(start, maxLen);
	return maxLen;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值