暴力法
拿到题时就知道暴力法应该不行,但还是硬着头皮试了试,已经去想了尽可能快快一点的处理方式,但果然会超出时间限制
public class Solution
{
public string LongestPalindrome(string s)
{
HashSet<string> hs = new HashSet<string>();
int n = s.Length;
if(n==0) return "";
int min = n;
string ans = s[0].ToString();
for(int i=0;i<n;i++)
{
for(int j=n-1;j>=i;j--)
{
if(s[i]==s[j])
{
string temp = s.Substring(i,j-i+1);
hs.Add(temp);
if(temp.Length<min) min=temp.Length;
}
}
}
foreach (string temp in hs)
{
int i =temp.Length-2;
string retemp = temp[i+1].ToString();
for(;i>=0;i--)
{
retemp = retemp + temp[i];
}
if(retemp==temp&&temp.Length>=min)
{
min = temp.Length;
ans = temp;
}
}
return ans;
}
}
通过看题解,学习到了很多的方法:
动态规划
动态规划,即使用递归。算法的整体思想和暴力法一样,但在处理判断子字符串是否为回文时使用了递归。核心思想是:列一个二维表,把所有的情况列出来,记录是否为回文,但在不是对所有的子字符串都进行一次遍历,比如"bacab"和"aca",当判断完"aca"是回文时,只需判断"bacab"的头尾是否相等即可。但len=1和len=2是特殊情况,需要单独拿出来进行判断
以"babad"为例,给出二维表以方便理解。
public class Solution
{
public string LongestPalindrome(string s)
{
int n = s.Length;
bool[,] P = new bool[n, n];
string result = "";
for (int len = 1; len <= n; len++)
{
for (int start = 0; start < n; start++)
{
int end = start + len - 1;
if (end >= n) break;
//递归
P[start, end] = (len == 1 || len == 2 || P[start + 1, end - 1]) && s[start] == s[end];
if (P[start, end] && len > result.Length)
{
result = s.Substring(start, len);
}
}
}
return result;
}
}
中心扩展法
核心思想: 根据回文的特性,只要在子字符串以中心为轴,利用双指针判断两边的元素是否都是对称的,如果对称那么此子字符串就是回文。
(中心扩展法是我第一个想到的解决方法,但限于对编程的理解有限,觉得这个方法应该不可行,因为那个轴很难找。现在想来,一开始我是想要一下子就找到最长的子字符串的轴,这确实很很难实现,但如果是遍历整个s,多次取轴那么中心扩展法就可以实现。)
细节处理: 对于奇数情况的子字符串,是很好理解的;但对于偶数情况,就需要一些细节处理技巧,这里是直接把偶数的对称线作为轴,有的方法是在字符串中加入特殊字符"@""$"如此,化奇为偶,但个人感觉比较复杂,倾向于直接把对称线当成轴。这里直接把字符串进行划分。
public class Solution {
public string LongestPalindrome(string s)
{
string result = "";
int n = s.Length;
int end = 2 * n - 1;
for (int i = 0; i < end; i ++)
{
double mid = i / 2.0;//划分出偶数情况轴的位置
int p = (int)(Math.Floor(mid));//舍去mid中的小数,左指针
int q = (int)(Math.Ceiling(mid));//如果mid中含小数,舍去小数后加一,右指针
while (p >= 0 && q < n)
{
if (s[p] != s[q]) break;
p--; q++;
}
int len = q - p - 1;
if (len > result.Length)
result = s.Substring(p + 1, len);
}
return result;
}
}
Manacher‘s Algorithm
这个算法不是很明白,整体过程和处理能明白,但还是很难理解
public string PreProcess(string s)
{
string t = "";
int n = s.Length;
if (n == 0) return "";
for (int i = 0; i < n; i++)
{
t += "#" + s[i];
}
t += "#";
return t;
}
public string LongestPalindrome(string s)
{
string t = PreProcess(s);
int n = t.Length;
int[] p = new int[n];
int c = 0, r = 0
for (int i = 1; i < n - 1; i++)
{
int j = 2 * c - i;
p[i] = r > i ? Math.Min(r - i + 1, p[j]) : 1;
while (i + p[i] < n && i - p[i] >= 0)
{
if (t[i - p[i]] == t[i + p[i]]) p[i]++;
else break;
}
if (i + p[i] > r)
{
//找到了更长的回文串,更新c和r
c = i;
r = i + p[i] - 1;
}
}
// 找出 P 的最大值
int maxLen = 0;
int centerIndex = 0;
for (int i = 1; i < n - 1; i++)
{
int len = p[i] - 1;
if (len > maxLen)
{
maxLen = len;
centerIndex = i;
}
}
int start = (centerIndex - maxLen) / 2;
return s.Substring(start, maxLen);
}