@Leetcode验证回文串
笔者的记忆中,不久前曾经有一道回文数的题,笔者想了想回顾了一下,然而对于回文串的处理可不止回文数的算法而已。请看题干:
给定一个字符串,验证它是否是回文串,只考虑字母和数字字符,可以忽略字母的大小写。
说明:本题中,我们将空字符串定义为有效的回文串。
示例 1:
输入: “A man, a plan, a canal: Panama”
输出: true
示例 2:
输入: “race a car”
输出: false
思路简单的方法自然不用多说,很慢。笔者当然不会错过这种很快就能想到的方法,一份千疮百孔(WA了大概20次)的代码交了上去,请看:
class Solution {
public:
bool isPalindrome(string s) {
char str[1000000];
memset(str,0,sizeof(str));
int size=s.size();
if(size==0)
return true;
for(int i=0,j=0;i<size;i++){
if(s[i]>='A'&&s[i]<='Z'){
str[j]=s[i]+32;
j++;
}
else if(s[i]>='a'&&s[i]<='z'){
str[j]=s[i];
j++;
}
else if(s[i]>='0'&&s[i]<='9'){
str[j]=s[i];
j++;
}
}
int cou=strlen(str);
if(cou==0)
return true;
for(int i=0;i<=(strlen(str)-1)/2;i++){
if(str[i]!=str[strlen(str)-i-1]){
return false;
}
}
return true;
}
};
这份代码笔者实在是漏洞百出,这就给各位一一说来:
首先需要注意,既然我们要将字符都转换存取,那么这个数组一定不能开小,笔者这边给到建议是直接开1e6的数组来的十分直接,这也是笔者开了几个小几百和小几千的数组之后发现的问题。
其次,要将准换后的大数组清空,memset就够了也不用多,并且检查自己的下标有没有问题,如果在这里出现问题,很可能你只能通过83个样例。
再其次,s为空和s只有一个字符,这两个情况需要特判,或者你得判断条件中包含了这两个也行,否则你会发现第95个样例无法通过。
再再其次,请注意检查你是否把数字的判断也写入了转换代码,如果没有,那么第129个样例必然会拦住你。
最后,我们看看你有没有犯一些细节的错误,比如说memset里面参数写反了(笔者智障),比如说true false写反了或者判断条件刚好反了之类的,否则你就会像笔者一样在过了131个样例之后不知所措。
如果上面的问题你都没有,那么恭喜,你成功用 一个极高耗时的方法写出了这个和回文数相比就多了一个转换操作的回文串的代码。这么说只有一个意思,没错,肯定有更快的方法,而且不止快一点点。请看代码:
class Solution {
public:
bool isPalindrome(string s) {
if(s.size()<=1) return true;
int i=0;
int j=s.size()-1;
while(i<j){
while(i<j&&!isalnum(s[i]))
i++;
while(i<j&&!isalnum(s[j]))
j--;
if(tolower(s[i++])!=tolower(s[j--]))
return false;
}
return true;
}
};
这份代码告诉大家,并不是循环层数少,时间复杂度就低的。这个方法人称双指针库函数法,使用到了c++中十分经典的两个库函数:isalnum和tolower,前者能够判断是否是字母,能够将所有读取的字符中的字母筛选出来,后者则能够将所有的字母转换为小写再进行判断。
双指针法用两个标志分别指向转换后数组的头尾并逐渐靠近进行判断,虽说思路和前一种大同小异,然而在时间复杂度上可谓碾压,代码整洁工整,笔者也颇受指点才明白这个方法,希望大家联系回文数的算法看看,定能有所收获。