字符串篇
1.反转字符串(leecode第344题)
编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 char[] 的形式给出。不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。
你可以假设数组中的所有字符都是 ASCII 码表中的可打印字符。
1.1 双指针法
算法:就是将头尾的元素互换,双指针,start和end一个向前一个向后。
class Solution {
public:
void reverseString(vector<char>& s) {
int start=0;
int end=s.size()-1;
while(start<end)
{
swap(s[start],s[end]);
start++;
end--;
}
}
};
1.2 reverse法
算法:c++自带reverse反转函数,string.begin()和end()返回的都是迭代器iterator位置。
class Solution {
public:
void reverseString(vector<char>& s) {
reverse(s.begin(),s.end());
}
};
1.3递归法
算法:其实和第一个方法相似,只不过用递归来实现。
class Solution {
public:
void reverseString(vector<char>& s) {
fun(0,s.size()-1,s);
}
void fun(int start,int end, vector<char>& s)
{
if(start>end)
return;
char temp=s[start];
s[start]=s[end];
s[end]=temp;
fun(start+1,end-1,s);
}
};
2.整数反转(leecode第7题)
题目:给出一个 32 位的有符号整数,你需要将这个整数中每位上的数字进行反转。
示例 1: 示例 2: 示例 3:
输入: 123 输入: -123 输入: 120
输出: 321 输出: -321 输出: 21
注意:
假设我们的环境只能存储得下 32 位的有符号整数,则其数值范围为 [−231, 231 − 1]。请根据这个假设,如果反转后整数溢出那么就返回 0。
2.1整除法
算法:通过将数字通过对10取余,整数除法拆分数字,后再反向组合。
class Solution {
public:
int reverse(int x) {
int symbol=0,temp;
long res=0,x_l=x;
if(x_l<0) //保存符号
{
symbol=1;
x_l=-x_l; // 这里必须将x从int转换成long型,不然会出错
}
while(x_l/10>0)
{
temp=x_l%10;
res=res*10+temp;
x_l/=10;
}
res=res*10+x_l;
if(symbol==1)
res=-res;
if(res>(pow(2,31)-1) || res<-pow(2,31)+1)
return 0;
else
return res;
}
};
看了官方解题,其实解题思路和我一致,但是很多地方做的比我好,例如int型变量的范围不用自己算,可以用INT_MAX,INT_MIN表示;还有我是处理不好负数整除,所以把符号提出来,官方则没有,附上c++整数除法取模知识的参考链接;我是把数放在long型变量里处理防止溢出,官方则直接判断。
补充个小知识:
int整数 取余是向零取整的
取模: 余数=被除数-商×除数 (*)
7/(-4)=-1; -7/4=-1;
7%(-4)=3; -7%4=-3;
对于有符号整数与无符号整数间的除法,C/C++会将有符号整数转换为无符号整数,
需要特别注意的是,符号位并没有丢失,而是变成了数据位参与运算。这就是
(-7)/(unsigned)4不等于-1,而等于1073741822的原因。
附上官方解题
class Solution {
public:
int reverse(int x) {
int rev = 0;
while (x != 0) {
int pop = x % 10;
x /= 10;
if (rev > INT_MAX/10 || (rev == INT_MAX / 10 && pop > 7)) return 0;
if (rev < INT_MIN/10 || (rev == INT_MIN / 10 && pop < -8)) return 0;
rev = rev * 10 + pop;
}
return rev;
}
};
下面这个是结合起来运行最快的方法,运行0ms,本来是用x_l=labs(x),但是很花时间。labs()函数是对long型取绝对值,输出也是long型变量。
class Solution {
public:
int reverse(int x) {
int sign=x<0? -1:1;
long res=0,x_l=x;
if(x<0)
x_l=-x_l; //这里如果是x_l=-x,-INT_MAX还是溢出的
while(x_l)
{
res=res*10+x_l%10;
x_l/=10;
}
res=sign*res;
if(res>INT_MAX || res<INT_MIN)
return 0;
return res;
}
};
2.2字符法
算法:将数字转换为字符处理,通过sprintf函数将数字转为字符,这里sprintf的用法得注意。注意几点:
C 库函数 int sprintf(char *str, const char *format, ...) 发送格式化输出到 str 所指向的字符串。
1、如果数字-123通过sprintf转为字符,字符串s[0]='-'
2、sprintf转为字符后,多余的位置值会改变
3、sprintf函数返回值是写入了多少位字符,后面没写入会追加空字符
4、这题x输入最大的字符数应该是11位,但是我给s[11]空间是有问题的,s[12]就可以,不
知道为啥,待解
class Solution {
public:
int reverse(int x) {
char s[12]={'0'};
long res=0;
int n=sprintf(s,"%d",x);
for(int i=n;i<11;i++)
s[i]='0';
if(x<0)
{
for(int i=10;i>0;i--)
res=res*10+s[i]-'0';
res=-res;
}
else
for(int i=10;i>=0;i--)
res=res*10+s[i]-'0';
if(res>INT_MAX || res<INT_MIN)
return 0;
return res;
}
};
3.字符串中的第一个唯一字符(leecode第387题)
题目:给定一个字符串,找到它的第一个不重复的字符,并返回它的索引。如果不存在,则返回 -1。
案例:
s = "leetcode" s = "loveleetcode",
返回 0. 返回 2.
注意事项:您可以假定该字符串只包含小写字母。
3.1 哈希法
算法:通过map建立哈希表,存入字符个数,然后输出第一个个数唯一的字符下标。这里想说一下,如下程序建立的map没有初始化,第二个量为什么默认初始化为零。
class Solution {
public:
int firstUniqChar(string s) {
map<char,int> table;
int n=s.size();
for(int i=0;i<n;i++)
table[s[i]]++;
for(int i=0;i<n;i++)
if(table[s[i]]==1)
return i;
return -1;
}
};
4. 有效的字母异位词(leecode第242题)
给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的一个字母异位词。
示例 1:
输入: s = "anagram", t = "nagaram"
输出: true
示例 2:
输入: s = "rat", t = "car"
输出: false
说明:
你可以假设字符串只包含小写字母。
进阶:
如果输入字符串包含 unicode 字符怎么办?你能否调整你的解法来应对这种情况?
4.1 map哈希法
算法:通过map建立t和s的哈希表,通过查找个数是否一致判断结果
class Solution {
public:
bool isAnagram(string s, string t) {
map<char,int> s_table,t_table;
for(int i=0;i<s.size();i++)
s_table[s[i]]++;
for(int i=0;i<t.size();i++)
t_table[t[i]]++;
for(int i=0;i<t.size()|| i<s.size();i++)
if(t_table[t[i]]!=s_table[t[i]] ||t_table[s[i]]!=s_table[s[i]])
return false;
return true;
}
};
4.2 数组哈希法
算法:通过数组建立哈希表,参考别人的答案,该方法速度比较快
class Solution {
public:
bool isAnagram(string s, string t) {
int a[26]={0},b[26]={0};
int i;
for(i=0;s[i]!='\0';i++) //建立哈希表s
a[s[i]-'a']++;
for(i=0;t[i]!='\0';i++)
b[t[i]-'a']++;
for(i=0;i<26;i++)
if(a[i]!=b[i])
return false;
return true;
}
};
4.3 排序法
算法:排序后比较,该题就是判断两个字符串是否相同
class Solution {
public:
bool isAnagram(string s, string t) {
int i;
sort(s.begin(),s.end());
sort(t.begin(),t.end());
for(i=0;s[i]!='\0' && t[i]!='\0';i++)
if(s[i]!=t[i])
return false;
if(s[i]!='\0' || t[i]!='\0')
return false;
return true;
}
};
5.验证回文字符串(leecode第125题)
5.1双指针法
算法:题目比较简单,只要把大小写处理下还有非字母和数字的字符排除,然后首尾比较就好了,唉,但是我在做的时候因为把判断大小写时的=给忘了,折磨我好久。不要忘了等于,不要忘了等于,不要忘了等于!重要的事情说三遍!
class Solution {
public:
bool isPalindrome(string s) {
if(s.size()==0)
return true;
int start=0;
int end=s.size()-1;
while(start<end)
{
if(s[start]<='Z' && s[start]>='A')
s[start]+='a'-'A';
if(s[end]<='Z' && s[end]>='A')
s[end]+='a'-'A';
if( !((s[start]>='a' && s[start]<='z') || (s[start]>='0' &&s[start]<='9')) )
{
start++;
continue;
}
if( !((s[end]>='a' && s[end]<='z') || (s[end]>='0' &&s[end]<='9')) )
{
end--;
continue;
}
if(s[start]==s[end])
{
start++;
end--;
}
else
return false;
}
return true;
}
};
6.字符串转换整数(leecode第8题)
感觉这题好蠢啊,没什么营养,一直是不同测试案例调,也可能是我思路错了,不想弄了,下面是调一半的程序,这个程序是对+,-,空格,数字都设置了标志位,分别考虑,且参考plc的方法考虑互锁。
已经调好了,下面的是可以运行了,设置了一个accept接受标志。
class Solution {
public:
int myAtoi(string str) {
long res=0;
int sign=1;
int accept=0;
if(str.size()==0)
return 0;
for(int i=0;i<str.size();i++)
{
if(str[i]=='-')
{
if(accept==1)
return res*sign;
sign=-1;
accept=1;
}
else if(str[i]=='+')
{
if(accept==1)
return res*sign;
sign=1;
accept=1;
}
else if(str[i]==' ')
{
if(accept==0)
continue;
if(accept==1)
return res*sign;
}
else if(str[i]>='0' && str[i]<='9')
{
accept=1;
if(sign==1 && (res>INT_MAX/10 || (res==INT_MAX/10 && str[i]-'0'>7)) )
return INT_MAX;
if(sign==-1 && (res>INT_MAX/10 || (res==INT_MAX/10 && str[i]-'0'>8)) )
return INT_MIN;
res=res*10+str[i]-'0';
}
else
return res*sign;
}
return res*sign;
}
};
7.实现strstr()
题目:实现 strStr() 函数。
给定一个 haystack 字符串和一个 needle 字符串,在 haystack 字符串中找出 needle 字符串出现的第一个位置 (从0开始)。如果不存在,则返回 -1。
示例 1:
输入: haystack = “hello”, needle = “ll”
输出: 2
示例 2:
输入: haystack = “aaaaa”, needle = “bba”
输出: -1
说明:
当 needle 是空字符串时,我们应当返回什么值呢?这是一个在面试中很好的问题。
对于本题而言,当 needle 是空字符串时我们应当返回 0 。这与C语言的 strstr() 以及 Java的 indexOf() 定义相符。
7.1暴力法
直接遍历所有情况,但是超时。
class Solution {
public:
int strStr(string haystack, string needle) {
int h=haystack.size();
int n=needle.size();
int temp;
if(n==0)
return 0;
for(int i=0;i<h;)
{
temp=i;
for(int j=0;j<n;)
{
if(haystack[i]==needle[j])
{
i++;
j++;
}
else
break;
if(j==n)
return temp;
}
i=temp+1;
}
return -1;
}
};
7.2 内置函数法
class Solution {
public:
int strStr(string haystack, string needle) {
if(needle.empty()) return 0;
int pos=haystack.find(needle);//在字符串haystack中查找needle
return pos; //未找到find()返回-1
}
};
7.3KMP方法
唉写不动了,这个我之前写过,参考我这篇博客吧。
用c++具体实现的KMP模式匹配算法
class Solution {
public:
int strStr(string haystack, string needle) {
return KMP(haystack,needle);
}
void get_next(string &T,vector<int> &next)
{
int i,j;
i=0;
j=-1;
next.push_back(-1);
int tmp=T.size();
while(i<tmp)
{
if(j==-1 || T[i]==T[j])
{
++j;
i++;
next.push_back(j);
}
else
j=next[j];
}
}
int KMP(string &x,string &T)
{
vector<int> next;
int i=0,j=0;
get_next(T,next);
int tmp2=x.size();
int tmp1=T.size();
while(i<tmp2 && j<tmp1)
{
if(j==-1 || x[i]==T[j] ) //这里的两个条件不能互换,一换就错了
{ //这里折磨了我半天,难受
i++;
j++;
}
else
j=next[j];
}
if(j==tmp1)
return i-j;
else
return -1;
}
};
8. 报数(leecode第38题)
报数序列是一个整数序列,按照其中的整数的顺序进行报数,得到下一个数。其前五项如下:
- 1
- 11
- 21
- 1211
- 111221
1 被读作 “one 1” (“一个一”) , 即 11。
11 被读作 “two 1s” (“两个一”), 即 21。
21 被读作 “one 2”, “one 1” (“一个二” , “一个一”) , 即 1211。
给定一个正整数 n(1 ≤ n ≤ 30),输出报数序列的第 n 项。
注意:整数顺序将表示为一个字符串。
示例 1: 示例 2:
输入: 1 输入: 4
输出: "1" 输出: "1211"
8.1递归法
算法:递归法,给定一个初始项,通过递归得到一个第n-1项,再用这个第n-1项算出第n项return 结果
class Solution {
public:
string countAndSay(int n) {
if(n==1)
return "1";
string res=countAndSay(n-1);
string result;
char count='1';
char temp=res[0];
int j=0;
for(int i=1;i<res.size();i++)
{
if(temp==res[i])
count++;
else
{
result.push_back(count);
result.push_back(temp);
temp=res[i];
count='1';
}
}
result.push_back(count);
result.push_back(temp);
return result;
}
};
下面这个程序原理和上面一样,不过在处理保存字符有点不同,注意到to_string是将数字转换成字符串,这里可以学习一下c++ string用+的连接,刚开始我用string+=char+char这种格式,是不对的。
对于string类型变量,我们可以直接用“+”或者“+=”进行字符串的连接,操作符非常方便。
用“+”风格字符串进行字符串连接时,操作符左右两边既可以都是string字符串,也可以是一个string字符串和一个C风格的字符串,还可以是一个string字符串和一个char字符。
而用“+=”风格字符串进行字符串连接时,操作符右边既可以是一个string字符串,也可以是一个C风格字符串或一个char字符。
string+=char可以,但是string+=char+char是不对的,改成string=string+char+char可以,所以=右边必须有一个string或者只能有一个char。
class Solution {
public:
string countAndSay(int n) {
if(n==1)
return "1";
string res=countAndSay(n-1);
string result;
int count=1;
char temp=res[0];
int j=0;
for(int i=1;i<res.size();i++)
{
if(temp==res[i])
count++;
else
{
result=result+to_string(count)+temp;
temp=res[i];
count=1;
}
}
result+=to_string(count);
result+=temp;
return result;
}
};
9. 最长公共前缀(leecode第14题)
编写一个函数来查找字符串数组中的最长公共前缀。
如果不存在公共前缀,返回空字符串 “”。
示例 1:
输入: [“flower”,“flow”,“flight”]
输出: “fl”
示例 2:
输入: [“dog”,“racecar”,“car”]
输出: “”
解释: 输入不存在公共前缀。
说明: 所有输入只包含小写字母 a-z 。
9.1水平扫描法
算法:将第一个字符串作为参考,将strs后面的每个字符串依次和他比较,算出公共前缀。
(1)string& erase ( size_t pos = 0, size_t n = npos );//默认参数是0到结尾
(2)iterator erase ( iterator position );
(3)iterator erase ( iterator first, iterator last );
class Solution {
public:
string longestCommonPrefix(vector<string>& strs) {
if(strs.empty()) return ""; //如果容器vector为空,则返回“”
string res=strs[0];
for(int i=1;i<strs.size();i++)
for(int j=0;j<res.size();j++)
if(res[j]==strs[i][j])
continue;
else
{
res.erase(j);
break;
}
return res;
}
};
9.2 水平扫描法
算法:上面是将两个字符串两个字符串的比较,下面是从每个字符串的首字符开始比较,得出结果
class Solution {
public:
string longestCommonPrefix(vector<string>& strs) {
if(strs.empty()) return "";
string res;
for(int i=0;i<strs[0].size();i++)
{
char t=strs[0][i];
for(int j=1;j<strs.size();j++)
{
if(strs[j][i]!=t|| i == strs[j].size())
return res;
}
res.push_back(strs[0][i]);
}
return res;
}
};
9.3 分治法
算法:该方法就是将第一种方法进行递归运算,和分治排序法的思想相似。
class Solution {
public:
string longestCommonPrefix(vector<string>& strs) {
if(strs.empty())
return "";
return marge(strs,0,strs.size()-1);
}
string marge(vector<string>& strs,int l,int r)
{
if(l==r)
return strs[r];
int mid=(l+r)/2;
string lstr=marge(strs,l,mid);
string rstr=marge(strs,mid+1,r);
string res=commonPrefix(lstr,rstr);
return res;
}
string commonPrefix(string lstr,string rstr)
{
string res;
for(int i=0;i<lstr.size() && i<rstr.size();i++)
{
if(lstr[i]!=rstr[i])
return res;
res.push_back(lstr[i]);
}
return res;
}
};
这里复习一下归并排序的方法
void marge_sort(vector<int>& num,int l,int r)
{
if(l==r)
return ;
int mid=(l+r)/2;
vector<int> lnum,rnum;
marge_sort(num,l,mid);
marge_sort(num,mid+1,r);
for(int i=0;i<=mid-l;i++) //对两部分已经排好序的数组进行合并
lnum[i]=num[i+l];
for(int i=0;i<=r-mid;i++)
rnum[i]=num[mid+i];
int j=0,k=0;
for(int i=l;i<=r;i++)
{
if(lnum[j]<=rnum[k])
{
num[i]=lnum[j];
j++;
}
else(lnum[j]>rnum[k])
{
num[i]=rnum[k];
k++;
}
}
字符串篇也结束,还会持续更新。