最长回文子串(C#)
题目描述:给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。
1. 暴力法
思路:对所有子串遍历,判断是否为回文串。
显然需要一个函数判断一个字符串是否为回文串
public bool isPalindromic(string s)
{
for (int i = 0; i < s.Length / 2; i++)
{
if (s[i] != s[s.Length - i - 1])
return false;
}
return true;
}
ok,接下来进行遍历
public string LongestPalindrome(string s)
{
string result = "";
int MaxLength = 0;
string current_s="";
for (int i = 0; i < s.Length; i++)
{
for (int j = i; j < s.Length; j++)
{
current_s=s.Substring(i, j - i + 1);
result = isPalindromic(current_s) && current_s.Length > result.Length ? current_s : result;
}
}
return result;
}
简单粗暴,但是时间复杂度很高,所以提交会这样:
但其实结果是对的:
时间复杂度:两层 for 循环已经是O(n2)了,在内层循环判断是否是回文串还需要O(n),所以是O(n3)了。
空间复杂度:常数个变量所以是O(1)。
2.中心扩展算法
比较常规的,也是我看到这道题的第一种想法。
思路:一个字符串的长度只能是奇数或者偶数(废话),那么一个回文字符串也只能是奇数的或者偶数的(还是废话)。奇数回文字符串的中心是一个字符,而偶数字符串的中心是一个 间隔 。
我们可以通过这个中心向两边扩展,使用左右两个指针(并不是指针),每次循环先判断两个指针对应位置的值是否相等,然后左指针向左移一个位置,右指针向右移一个位置,直到两个值不相等,或者越界。一次循环结束之后,移动中心的位置,需要注意的是中心只需要移动到字符串的最后一个位置即可。
重点来了! 因为奇数串和偶数串的中心不同,所以中心每次移动都需要判断两次。向isPalindromic函数输入不同值来区分奇数串和偶数串:
1、如果传入重合的索引编码,进行中心扩散,此时得到的回文子串的长度是奇数;
2、如果传入相邻的索引编码,进行中心扩散,此时得到的回文子串的长度是偶数。
可以理解为,奇数串的时候是以输入的位置i作为中心,而偶数串的时候是以i和i+1之间作为中心,也就是上面说的那个 间隔 。
代码如下:
public string LongestPalindrome(string s) {
if(s.Length<=1)
return s;
string result="";
for (int i = 0; i < s.Length - 1; i++)
{
string oddString = isPalindromic(s, i, i);//奇数串
string evenString = isPalindromic(s, i, i + 1);//偶数串
if (oddString.Length>result.Length||evenString.Length>result.Length)
result = oddString.Length > evenString.Length ? oddString : evenString;
}
return result;
}
public string isPalindromic(string s, int left, int right)
{
string result="";
while (left >= 0 && right < s.Length)
{
if (s[left] != s[right])
break;
result = right - left + 1 > result.Length ? s.Substring(left, right - left + 1) : result;
left--;
right++;
}
return result;
}
时间复杂度:两层 for 循环所以是O(n2)
空间复杂度:常数个变量所以是O(1)。
3.动态规划法
动态规划法的做法其实和暴力法很相似,但是不使用额外的函数去判断一个字符串是否回文,而是通过一个二维数组将之前得到的子串是否回文的结果保存起来。
思路:首先,一个字符串若是回文的,那么其两端的两个字符一定相等,其次除去两端的两个字符后中间的子串也必然是回文的。
所以我们定义一个二维数组P(i,j),里面存放的是对应从位置i到j的字符串是否是回文的。
根据前面所述,P(i,j)的值为(S[i]==S[j]&&P(i+1,j−1))。但是还有一点小问题,当j-i的值小于等于2(也就是当前判断的字符串长度为0,1,2)时,P(i+1,j−1)的值是不存在的。
所以应当表述为P(i,j) = (s[i] == s[j]) && (j - i <= 2 || P(i+1,j−1))。需要注意的是j-i<=2应当放在P(i+1,j−1)前判断。由于短路或(||)的性质,当j-i<=2成立时不会对后面的P(i+1,j−1)进行判断,也就巧妙地规避了P(i+1,j−1)的值不存在的问题。
代码如下:
public static string LongestPalindrome1(string s)
{
if (s.Length == 0 || s.Length == 1)
return s;
string result="";
bool[,] dp = new bool[s.Length, s.Length];
for (int right = 0; right < s.Length ; right++)
{
for (int left = 0; left <= right; left++)
{
dp[left, right] = (s[left] == s[right]) && (right - left <= 2 || dp[left + 1, right - 1]);
result = dp[left, right] && (right - left >= result.Length) ? s.Substring(left, right - left + 1) : result;
}
}
return result;
}
时间复杂度:两层循环 O(n2)。
空间复杂度:用二维数组 P 保存每个子串的情况 O(n2)。
来源:CSDN
作者:佐晓佐
链接:https://blog.csdn.net/qq_36390344/article/details/103630471