文章目录
一. 概念定义
回文串:回文串就是正向读取与反向读取结果是一样的,例如:abcba,正向读取结果为abcba,反向读取的结果也为abcba,这样的字符串即为回文串。
二. 判断方法
我们不难发现,回文串有这样一个特性,首尾字符相等,所以我们可以分别从头部和尾部同时向中间进行判断,查看两端的字符是否相等,如不相等,则非回文串。
如图:
代码如下:
#include <stdio.h>
#include <stdbool.h>
bool isPalindrome(char* s){
int left = 0, right = strlen(s) - 1;
while (left < right){
//如果出现两端对称不相等的字符,则返回false
if (s[left++] != s[right--]){
return false;
}
}
return true;
}
int main(){
char s1[] = "abcba";
char s2[] = "sdasc";
if (isPalindrome(s1)){
printf("s1是回文串\n");
}
else{
printf("s1不是回文串\n");
}
if (isPalindrome(s2)){
printf("s2是回文串\n");
}
else{
printf("s2不是回文串\n");
}
return 0;
}
我们可以看到打印结果为:
三. 课后习题
3.1 回文排列
题目链接:
面试题 01.04. 回文排列
分析:
题目要求是给定一个字符串,判断这个字符串是否能够通过调换顺序,使得其变成一个回文串,而一个回文串的特点是,它一定存在一个中间分割线,并且回文串关于此线对称,所以回文串的字符一定是成对存在的,但也都特殊情况,当字符串长度为奇数时,最中间的字母就是分割线,所以它可以是单独存在的。题目并未说明不区分大小写,那我们就默认区分大小写。
代码思路:
- 首先我们需定义一个数组Hash[123],用于记录每个字符出现的次数。
- 判断是否存在奇数数目的字符,并用cnt统计有多少种单独存在的字符
- 根据两种情况判断是否为回文串:
(1)字符串长度为偶数,那所以字符一定是成对存在的,即(n%2==0 && cnt == 0)
(2)字符串长处为奇数,那么允许存在一个单独存在的字符,即(n % 2 && cnt <= 1)
代码如下:
bool canPermutePalindrome(char* s){
int n = strlen(s);
int Hash[123] = { 0 };
//记录出现每个字符出现的次数
for(int i = 0; i < n; i++){
Hash[s[i]]++;
}
int cnt = 0;
//记录有多少种单独存在的字符
for(int i = 0; i < 123; i++){
if(Hash[i] % 2 == 1){
cnt++;
}
}
//判断是否为回文串
if(n % 2 && cnt <= 1){
return true;
}
else if(n % 2 == 0 && cnt == 0){
return true;
}
return false;
}
3.2 有效的回文
题目链接:
剑指 Offer II 018. 有效的回文
分析:
题目要求是给定一个字符串,只考虑字母和数字字符,并且不区分大小写,判断是否为回文串。
思路:
这一题的难度就在于如何将除字母和数字以外的字符除去,我们可以通过双指针的方式,将所有不需要判断的字符覆盖。
借鉴与这种方法:
双指针
代码如下:
//判断数字字符
bool isNumber(int a){
if(a >= '0' && a <= '9')return true;
return false;
}
//判断字母,并将大写转化为小写
bool isAlphabet(char* ch){
if(*ch >= 'A' && *ch <= 'Z')
{
*ch += 32;
return true;
}
else if(*ch >= 'a' && *ch <= 'z')return true;
return false;
}
bool isPalindrome(char * s){
int n = strlen(s);
int slow = 0, fast = 0;
//将除数字字符和字母以外的字符去除
while(fast < n){
if(isNumber(s[fast]) || isAlphabet(&s[fast])){
s[slow++] = s[fast++];
}
else{
fast++;
}
}
int left = 0, right = slow - 1;
//判断是否为回文
while(left < right){
if(s[left++] != s[right--])return false;
}
return true;
}
3.3 验证回文串
题目链接:
125. 验证回文串
这道题和上面那道是一样的,但是多了一种情况,空字符串也为回文串,所以要对其进行特殊判断。
代码如下:
//判断数字字符
bool isNumber(int a){
if(a >= '0' && a <= '9')return true;
return false;
}
//判断字母,并将大写转化为小写
bool isAlphabet(char* ch){
if(*ch >= 'A' && *ch <= 'Z')
{
*ch += 32;
return true;
}
else if(*ch >= 'a' && *ch <= 'z')return true;
return false;
}
bool isPalindrome(char * s){
int n = strlen(s);
//特殊情况
if(n == 0)return true;
int slow = 0, fast = 0;
//将除数字字符和字母以外的字符去除
while(fast < n){
if(isNumber(s[fast]) || isAlphabet(&s[fast])){
s[slow++] = s[fast++];
}
else{
fast++;
}
}
int left = 0, right = slow - 1;
//判断是否为回文
while(left < right){
if(s[left++] != s[right--])return false;
}
return true;
}
3.4 最长回文串
题目链接:
409. 最长回文串
分析:
题目要求是找出最长的回文串,也就是使用给定的字符串中的字符,所能够组成的最长字符串。
代码思路:
- 首先我们还是用一个数组Hash[],记录每种字符的数量。
- 然后根据回文字符的对称性,我们用一个变量len统计所有的偶数数量的字符,对于奇数数量的字符则-1再加入len中。
代码如下:
int longestPalindrome(char * s){
int n = strlen(s);
int Hash[123] = { 0 };
//记录字符数量
for(int i = 0; i < n; i++){
Hash[s[i]]++;
}
int cnt = 0;
int len = 0;
//判断奇偶
for(int i = 0; i < 123; i++){
if(Hash[i] % 2){
cnt += Hash[i];
len += (Hash[i] - 1);
}
else if(Hash[i] % 2 == 0 && Hash[i])len += Hash[i];
}
//判断是否有奇数数量的字符
if(cnt == 0){
return len;
}
return len + 1;
}
3.5 最多删除一个字符得到回文
剑指 Offer II 019. 最多删除一个字符得到回文
分析:
根据题目意思,最多只能进行一次减去字符的操作,所以当字符串长度为奇数时,它可以最多能存在三个单独字符,并且第三个一定在中间,当为偶数时,则这能存在两个单独的字符。
代码思路:
通过双指针的方式从首尾同时判断,如果遇到两端不相等的字符,则有两种方式,一是删除左边的,二是删除右边的,通过回溯的方式实现。
代码如下;
bool Recall(char* s, int left, int right, int change){
if(left >= right){
return true;
}
while(left < right){
if(s[left] != s[right]){
if(change == 0)
return false;
else{
return Recall(s, left+1, right,0)
|| Recall(s, left, right-1, 0);
}
}
left++;
right--;
}
return true;
}
bool validPalindrome(char * s){
return Recall(s, 0, strlen(s) - 1, 1);
}
3.6 验证回文字符串 Ⅱ
题目链接:
680. 验证回文字符串 Ⅱ
这道题和刚才上面的那道题是一样的。
代码如下:
bool Recall(char* s, int left, int right, int change){
if(left >= right){
return true;
}
while(left < right){
if(s[left] != s[right]){
if(change == 0)
return false;
else{
return Recall(s, left+1, right,0)
|| Recall(s, left, right-1, 0);
}
}
left++;
right--;
}
return true;
}
bool validPalindrome(char * s){
return Recall(s, 0, strlen(s) - 1, 1);
}
3.7 删除回文子序列
题目链接:
1332. 删除回文子序列
分析:
这道题乍一看好像很难,完全没有头绪,一开始大多数人肯定会想,这么难得题目怎么能叫简单题呢?但仔细一看,真的是简单题。体验就在于第一句话,字符串只由a和b组成,删除回文子串。
如果该串不是回文串,那我们只需要两步,第一步删除所有a(或b),那第二次就是删除所有b(或a),如果是子串,那只需1次删除。
代码如下:
int removePalindromeSub(char * s){
int left = 0;
int right = strlen(s) - 1;
if(right + 1 == 0){
return 0;
}
while(left < right){
if(s[left] != s[right]){
return 2;
}
left++;
right--;
}
return 1;
}