1.如何判断回文?
aba abba分奇数偶数;
2.题型
a:无重复字符的最长子串,利用hash[128]统计每个字符,双指针left 和right,如果hash[s[right]] == 0 就是没出现,计数++,right++;hash[s[right]] = 1;比较res和cnt最大值;
如果hash[s[right]] != 0,hash【left】 = 0;cnt–,left++;
b: 最长回文串
// 其实这是个数学题,通过给定的字符串s,构造出来的最长回文字符串,长度肯定小于等于s,如果小于s,那么最长回文串b的长度必然是奇数。
int longestPalindrome(char * s)
{
int c[128]={0};
int ret=0;
for(int i=0;i<strlen(s);i++)
{
c[s[i]]++;
}
for(int i=0;i<128;i++)
{
if (c[i] % 2 == 0){
ret =ret + c[i];
}else {
ret = ret + (c[i] -1);//-1
}
}
if( ret != strlen(s)){
ret = ret + 1;
}
return ret;
}
题目描述
解法一:常规暴力解法:
遍历字符串
1.对于每个字母,向两侧扩散,判断是否回文子串;
2.若为回文子串,保存最长的子串信息
3.子串长度为奇数或偶数,需分别判断
char * longestPalindrome(char * s){
int N = strlen(s), start = 0, len = 0; // N 字符串长度, start 子串起始位置, len 子串长度
for (int i = 0; i < N; i++) { // 奇数长度的回文子串
int left = i - 1, right = i + 1;
while (left >= 0 && right < N && s[left] == s[right]){
left--; right++; // 以 i 为中心,向两侧延伸,直到不再满足回文
} // left+1 ~ right-1 则为以i为中心的最大回文子串
if (right - left - 1 > len) { // 若更长,则保存该子串信息
start = left + 1;
len = right - left - 1;
}
}
for (int i = 0; i < N; i++) { // 偶数长度的回文子串
int left = i, right = i + 1; // 以 i+0.5 为中心,向两侧延伸
while (left >=0 && right < N && s[left] == s[right]) {
left--, right++;
}
if (right - left - 1 > len) {
start = left + 1;
len = right - left - 1;
}
}
s[start + len] = '\0'; // 原地修改返回
return s + start;
}
解法二:动态规划
使用动态规划,依次检测长度为 2,3,4…s.length 的字符串是否为回文串。
1.对于字符串长度为 1 的字符串不用检测,一定是回文串;
2.对于字符串长度为 2,3 的字符串,只需要首尾字符相同即为回文串;
3.对于字符串长度大于 3 的字符串,则检测首尾字符,若相同,则与去掉首尾字符的字符串结果相同(保存在dp中)
char * longestPalindrome(char * s){
//回文串结束下标
int j;
//最长回文串长度
int maxLength = 1;
//回文串起始位置
int begin = 0;
int len = strlen(s);
// dp[i][j] 表示 s[i..j] 是否是回文串
bool dp[len][len];
//0.初始化:所有长度为 1 的子串都是回文串
for (int i = 0; i < len; i++) {
dp[i][i] = true;
}
//1.长度为 1 的一定是回文串,故长度从 2 开始
for(int l = 2; l <= len; l++){
//2.起始位置从 0 开始
for(int i = 0; i < len; i++){
///结束位置
j = i + l - 1;
//3.注意数组越界,注意 等于
if (j >= len) {
break;
}
//回文串首尾字母不相同
if (s[i] != s[j]) {
dp[i][j] = false;
} else {
//回文串小于 3 个字符( 3 个字符内首尾相等即为回文串)
if (j - i < 3) {
dp[i][j] = true;
} else {
//回文串大于两个字符
dp[i][j] = dp[i + 1][j - 1];
}
}
//4.只要 dp[i][L] == true 成立,就表示子串 s[i:L] 是回文,此时记录回文 长度 和 起始位置
if (dp[i][j] && l > maxLength) {
maxLength = l;
begin = i;
}
}
}
//设置结束位置
s[begin + maxLength] = '\0';
return s + begin;
}
解法三:中心扩散
对字符串中每一个元素遍历,并进行中心扩散,同时记录最大值
/*
对当前中心扩散
@s :要判断的字符串
@left:中心往左走
@right:中心往有走
@ans:保存最长结果
*/
void extend(char *s,int left,int right,int* ans)
{
//如果越界直接跳出
if(left<0 && right>=strlen(s))
{
return ;
}
//进行中心扩散
//如果没有越界并且当前位置两个字符串相等则将left左移right右移
while(left>=0 && right<strlen(s) && s[left]==s[right])
{
left--;
right++;
}
//记录最大值
if(right-left > ans[1]-ans[0])
{
ans[1] = right;
ans[0] = left;
}
return ;
}
char * longestPalindrome(char * s){
int ans[2] = {0,0};
for(int i = 0; i < strlen(s); i++)
{
extend(s,i,i,ans);//一个值为中心的情况
extend(s,i,i+1,ans);//两个值为中心的情况
}
//保存返回值
char* r = (char*)malloc(ans[1]-ans[0]);
strncpy(r, s+ans[0] + 1 ,ans[1] - ans[0] - 1);
r[ans[1] - ans[0]-1] = 0;
return r;
}
中心扩散法
使用中心扩散法寻找回文数组,但是回文数组有两种可能,奇数个和偶数个。
设置两个指针left和right,分别位于对称中心的两边,向两边扩散,中心对称点是重复字符串则当成一个字符处理。
char * longestPalindrome(char * s){
if(strlen(s)==0||strlen(s)==1) return s;
int i,start,left,right,count,len;
start = len =0;
for(i=0;s[i]!='\0';i+=count){
count = 1;
left=i-1;
right = i+1;
while(s[right]!='\0'&&s[i]==s[right]){ //处理重复字符串
right++;
count++;
}
while(left>=0 && s[right]!='\0' && s[left] == s[right]){
left--;
right++;
}
if(right-left-1>len){
start = left+1;
len = right-left-1;
}
}
s[start + len] = '\0'; // 原地修改返回
return s + start;
}
char * longestPalindrome(char * s){
if (strlen(s) < 2){
return s;
}
int start = 0,end = 0;
for (int i =0 ;i< strlen(s);i++){
int temp_s = i,temp_e = i;//中间对称型 abcba
while(temp_s >=0 && temp_e < strlen(s) && s[temp_e] == s[temp_s]) {
temp_s--;
temp_e++;
}
temp_s++;// 最后一步不符合回退
temp_e--;
if(temp_e - temp_s > end - start){
start = temp_s;
end = temp_e;
}
if(i+1<strlen(s) && s[i] == s[i+1]) { //abba
int temp_s = i,temp_e = i+1;
while (temp_s >=0 && temp_e < strlen(s) && s[temp_e] == s[temp_s]) {
temp_s--;
temp_e++;
}
temp_s++;// 最后一步不符合回退
temp_e--;
if (temp_e - temp_s > end - start) {
start = temp_s;
end = temp_e;
}
}
}
char *sub_s = malloc((end -start +1) * sizeof(char *));
strncpy(sub_s, s+start, end - start +1);
sub_s[end -start +1] = '\0';
return sub_s;
}