【题目】
【方法一:暴力求解】 比如字符串"abcdca",长度为6。 先看有没有长度为6的回文子串。只需要检查"abcdca"即可。有就返回,没有就继续。 再看有没有长度为5的回文子串,只需要检查"abcdc"和"bcdca"即可。找到就返回,找不到再继续。 再看有么哦呦长度为4的回文子串,只需要检查3个串即可。找到就返回,找不到再继续。 ... 依次类推,肯定能输出一个结果。
代码:
结果:
结果不好。 时间复杂度为n^3,因为从len到1有一层循环,对于每个len又一层循环。判断是否是回文字符串又是一层循环。
该暴力法以后就不用了。
【方法二:中心扩展法】 略。时间复杂为o(n^2)
【方法三:动态规划】 设dp[i][j]记录从第i个位置到第j个位置的子串是否为回文串。 则有 dp[i][j]= true if dp[i+1][j-1] && s[i]==s[j] = false else
比如:要判断字符串"bdbad"是否为回文串。 dp[][]为:
首先长度为1的字符串一定是回文串。即,"b"、"d"、"b"、"a"、"d"一定是回文串。 所以dp[i][i]=1.
然后长度为2的回文串,只需要比较前后两个字符即可。即:if(S[i]=S[i+1]) dp[i][i+1]=1. 得到:
再看长度为3的回文串。比如abc。if(中间的是回文串,且前后两个字符相等) 则为true; 否则为false。判断中间是否为回文串,就用到了前面的结果。
依次类推。最后得到整个是否是回文串。当是回文串的时候,记录下长度和起始位置。最后输出即可。
代码:
结果:
时间复杂度为o(n^2)。
【简洁版本】 上面的思路很清晰,但是写起来比较麻烦,代码不是很简洁。是我自己写的。看了很多资料之后发现有下面这个简介的版本。 我上面的版本填写dp[][]的顺序是这样的:
而新的版本的顺序是这样的:
代码:
简介了很多。 重点在于以下代码: for(int i = n-1;i>=0;i--){ dp[i][i]=1; for(int j = i+1; j<n;j++){ if(S[i]==S[j] && i=j-1 || S[i]==S[j] && dp[i+1][j-1]==1){ dp[i][j]=1; .... } } }
问题是当求dp[i][j]的时候,dp[i+1][j-1]都求出来了么? 答案是是的。我们跑一下例子。"bdbad" (1)i=n-1。dp[i][i]=1。j=i+1,j=n,内部循环不执行。所以得到:
(2)i=n-2=3。dp[i][i]=1。
j=4<n。S[3]!=S[4]。dp[i][j]还是默认值0。
(3)i=2.dp[2][2]=1.
j=3。S[2]!=S[3]。dp[2][3]=0。
j=4。S[2]==S[4]=="a"。dp[i+1][j-1]=dp[3][3]=1。所以:dp[2][4]=1.
下面的就不写了。我们可以看到规律:
主对角线的元素是固定的1。次对角线的元素是我们通过判断S[i]==[j]&& j=i-1填进去的。其他元素依靠其左下角的元素进行判断。我们在求的时候是从下往上一行一行求的,所以当求d[i][j]时,它左下角的元素是已知的。
结果如下:
要记住这个简洁版本的代码。以后会经常用。以后不再使用自己写的麻烦的版本了。
【方法四:马拉车算法】
原理: https://blog.csdn.net/dyx404514/article/details/42061017 //这篇文章讲的很好。但是例子部分比较抽象。 https://blog.csdn.net/liuwei0604/article/details/50414542 //这篇文章例子给的很具体。
算法思想:
代码:
代码还可以优化优化,但是这样看起来逻辑清晰。
结果:
时间复杂度为o(n)
【总结】 本题一共给了四种经典的方法:暴力法、中心扩展法、动态规划法和马拉车算法。其中暴力法不再使用。后面三种方法需要掌握。 马拉车算法是效率最高的方法,但是应用场景较少。而中心 扩展法和动规的方法这种思想经常用到。
|