字符串
-
palindrome partitioning(回文分割)
题目描述:给定一个字符串s,分割s使得分割后的每个子字符串都是回文,返回字符串s的所有回文分割结果,比如s = “aab”
返回
[ [“aa”, “b”],
[“a”, “a”, “b”] ]知识点:子字符串表示:substr函数:substr(start, length)返回一个从start开始,长度为length的字符串
思路:要首先有一个能够判断是否为回文的函数,然后将所有结果表示出来。
class Solution{
public:
vector<vector<string>> partition(string s) {
vector<vector<string>> result;
vector<string> tmp;
recurPartition(s, 0, tmp, result);
return result;
}
private:
bool isPalindrome(string &s, int begin, int end){
int left = begin;
int right = end - 1;
//左右向中间逼近
while(left < right){
if(s[left++] != s[right++])
return false;
}
return true;
}
void recurPartition(string &s, int start, vector<string> &tmp, vector<vector<string>> &result){
if(start == s.size())
result.push_back(tmp);//当遍历到只剩一个元素,那么必然就是回文字符串了
for(int i = start + 1; i <= s.size(); i++){
if(isPalindrome(s, strat, i)){
tmp.push_back(s.substr(start, i - start));
recurPartition(s, i, tmp, result);
tmp.pop_back();//使用递归的方法,每当结束一次时,清空tmp
}
}
}
};
- simplify path(简化路径)
题目描述:给一个unix风格的文件的绝对路径,简化它。
例如;
path = “/home/”, => “/home”
path = “/a/./b/…/…/c/”, => “/c”
注意其中的一些特殊情况,比如: path = “/…/” => “/”;再比如: path = “/home//foo/” => “/home/foo”
知识点:(1) C++引入了ostringstream、istringstream、stringstream这三个类,要使用他们创建对象就必须包含sstream.h头文件,他们也可以用于string和int的类型转化。
istringstream是由一个string对象构造而来,从一个string对象读取字符。
ostringstream同样是有一个string对象构造而来,向一个string对象插入字符。
stringstream则是用于C++风格的字符串的输入输出的。
#include <iostream>
#include <sstream>
#include <string>
using namespace std;
int main(){
string test = "-123 9.98 welcome to, test!";
//istringstream iss; //istringstream提供读string的功能
//iss.str(test); //将string 类型的test赋值给iss,返回void
istringstream iss(test); //stringstream iss(test);效果是一样的
string s;
cout << "按照空格读取字符串" << endl;
while(iss >> s){
cout << s << endl;//按空格读取string
}
cout << "**" << endl;
ostringstream out;
out.put('t');//插入字符
out.put('e');
out << "st";
string res = out.str();//提取字符串;
cout << res << endl;
return 0;
}
输出结果为:
-123
9.98
welcome
to,
test!
**
test
(2) getline(cin, line, delim):getline()函数从标准输入读取整条记录到line中,一旦结束或者遇到delim,则终止。
本题代码
class Solution{
public:
string simplifyPath(string path) {
string res, tmp;
if(path.empty()) return res;
vector<string> stk;
stringstream ss(path);//将path赋值给字符流ss
//将字符流ss传给tmp,每遇到一次'/’,循环一次
while(getline(ss, tmp, '/')) {
if(tmp == "" || tmp ==".") continue;//如果是”//“或者开始时的"/"或者是一个”.“,都是无效的
if(tmp != "..") stk.push_back(tmp);//将有效的暂存于vector中
else if(!stk.empty()) stk.pop_back();//目录原则,每遇到".."都表示目录的上一层,所以要丢弃vector最后一个元素
}
for(auto str:stk) res+= "/" + str;//不断将结果加”/“
return res.empty() ? "/" : res;//如果结果为空,初始时并不为空,则要返回根目录"/”
}
}
- add binary (二进制相加)
题目描述:
给出两个用字符串表示的二进制数,返回他们的和(也用字符串表示)
例如:
a =“11”
b =“1”
返回"100".
分析:本题不适合直接将字符串转换为整数求解,因为是二进制求和,所以可以考虑从两个字符串的后面开始求和,用整除和取余分别表示进位和当前位
代码
class Solution {
public:
string addBinary(string a, string b) {
string ss;
int c = 0, a_i = a.size() - 1, b_i = b.size() - 1;//这里c表示的是进位
while(a_i >= 0 || b_i >= 0 || c == 1){
c += a_i >= 0 ? a[a_i--] - '0' : 0;//有的时候加a的对应项,否则0来补齐
c += b_i >= 0 ? b[b_i--] - '0' : 0;//这两步之后c最大为3,最小为0
ss = char(c % 2 + '0') + ss;//注意这里求和有先后顺序,无进位的相加,进位留给下一个循环
c /= 2;//进位与否给c
}
return ss;
}
};
- length of last word
题目描述
给出一个只包含大小写字母和空格的字符串s,请返回字符串中最后一个单词的长度
如果字符串中没有最后一个单词,则返回0
注意:单词的定义是仅由非空格字符组成的字符序列。
例如:
s =“Hello World”,
返回5。
(1)个人代码思路分析:可以考虑将字符串写入一个字符流中,逐个单词判断长度,不断覆盖前一个单词长度,注意末尾是空格的情况,需要返回前一个单词长度,添加判断条件排除,代码如下
class Solution {
public:
int lengthOfLastWord(const char *s) {
stringstream ss(s);
string line;
int res = 0;
while(getline(ss, line, ' ')) {
if(line.size() != 0)//排除末尾是一个空格的情况
res = line.size();
}
return res;
}
};
(2)leetcode高分代码思路分析:直接使用指针遍历,思路如出一辙
class Solution{
public:
int lengthOfLastWord(const char* s) {
int len = 0;
while (*s) {
if (*s++ != ' ')
++len;
else if (*s && *s != ' ')
len = 0;
}
return len;
}
};
- anagrams group
题目描述
给出一个字符串数组,将互为“换位词(anagrams)”的字符串组合在一起。
Given an array of strings, group anagrams together.
备注:所有的输入都是小写字母
Example:
Input: [“eat”, “tea”, “tan”, “ate”, “nat”, “bat”],
Output:
[
[“ate”,“eat”,“tea”],
[“nat”,“tan”],
[“bat”]
]
思路分析:使用一个map将所有字符一样的组合在一起。
class Solution {
public:
vector<vector<string>> groupAnagrams(vector<string>& strs) {
unordered_map<string, vector<string>> mp;
for (string s : strs) {
string t = s;
sort(t.begin(), t.end());
mp[t].push_back(s);
}
vector<vector<string>> anagrams;
for (auto p : mp) {
anagrams.push_back(p.second);
}
return anagrams;
}
};
- multiply strings
题目描述
给出两个用字符串表示的数字,将两个数字的乘积作为字符串返回。
备注:数字可以无限大,且是非负数。
思路分析:用乘法公式竖着列出两个数字相乘的方式
代码如下:
class Solution {
public:
string multiply(string num1, string num2) {
int m = num1.size(), n = num2.size();
string ans(m + n, '0');
for (int i = m - 1; i >= 0; i--) {
for (int j = n - 1; j >= 0; j--) {
int sum = (num1[i] - '0') * (num2[j] - '0') + (ans[i + j + 1] - '0');
ans[i + j + 1] = sum % 10 + '0';//个位相加
ans[i + j] += sum / 10;//进位
}
}
for (int i = 0; i < m + n; i++) {
if (ans[i] != '0') {
return ans.substr(i);
}
}
return "0";
}
};
- count and say
题目描述
count-and-say数列的前几项如下:
1, 11, 21, 1211, 111221, …
即:1th 1读作1个1,所以对应 11给到第二个数
2th 11读作2个1,所以对应的21给到第三个数
3th 21读作1个2,1个1,所以对应1211给到第四个数
以此类推到第n个数
要求:给出一个整数n,请给出序列的第n项
注意:序列中的数字用字符串表示
思路分析:这道题很明显可以看出第n项需要由第n-1项决定,所以是一道递归的题目
class Solution{
public:
string countAndSay(int n){
//先考虑特殊情况
if(n == 0) return "";
string result = "1";
while(n--){
string cur = "";//cur表示当前第几项的结果
for(int i = 0; i < result.size(); i++){
count = 1;//用来记录某个数字出现的次数
while((i+1 < result.size()) && (result[i] == result[i+1])){
count++;
i++;
}
cur += to_string(count) + result[i];
}
result = cur;
}
return result;
}
};
- implement strstr
题目描述
实现函数 strStr。
函数声明如下:
char *strStr(char *haystack, char *needle)
返回一个指针,指向needle第一次在haystack中出现的位置,如果needle不是haystack的子串,则返回null。
思路分析:本题就是判断一个字符串是不是另一个字符串子字符串的问题,注意的是指针的使用,短板,慎思之!现附上本人代码
class Solution {
public:
char *strStr(char *haystack, char *needle) {
int m = strlen(haystack),n = strlen(needle);
//string是STL的一个容器,用起来比较方便,可以直接转换
string ss = haystack;
string s = needle;
if(ss.empty() && s.empty()) return "";
if(m<n) return nullptr;
for(int i = 0; i < m; i++){
if(m - i >= n){
if(ss.substr(i,n) == s)
return haystack+i;
}
}
return nullptr;
}
};
当然,关于字符串匹配查找,存在一个经典的KMP算法(可以参考链接link),这个算法可以把n*m的时间复杂度降为线性的n+m,其代码如下
class Solution{
public:
int strStr(string haystack, string needle){
int m = haystack.size(), n = needle.size();
if(!n){
return 0;//如果为空,返回0
}
vector<int> pmt = kmpProcess(needle);
for(int i = 0, j = 0; i < m;) {
if(haystack[i] == needle[j]) {
i++, j++;
}
if(j == n){
return i - j;//如果所有位都相等,那么就找到了对应位置
}
if(i < m && haystack[i] != needle[j]){
j ? j = pmt[j - 1] : i++;//如果j!=0,那么j=pmt[j-1];否则i++
}
}
return -1;//如果没能找到,返回-1
}
private:
vector<int> kmpProcess(string needle) {
int n = needle.size();
//PMT(partial match table)部分匹配表,PMT中的值是字符串的前缀集合与后缀集合的交集中
//最长元素的长度,可以避免重复比较
//所谓前缀,必须包含第一个元素;所谓后缀,必须包括最后一个元素
vector<int> pmt(n, 0);
for(int i = 1, len = 0; i < n;) {
if(needle[i] == needle[len]) {
pmt[i++] = ++len;
} else if (len) {
len = pmt[len - 1];
} else {
pmt[i++] = 0;
}
}
return pmt;
}
};
KMP算法能大大减少字符串查找的复杂度,很好的思想,仔细体会
- 字符串的排列
题目描述
输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。
思路分析:本题的思路很简单,就是不断先去除其中一个字符,然后让剩余的字符串全排列,递归循环;不过本题还是有两点注意:首先要注意存在相同字符时的两个排序后的字符串相等的问题;其次,递归步骤如何书写,也需要一定编程功底,细细体会。代码如下:
class Solution {
public:
vector<string> Permutation(string str) {
vector<string> result;
if(str.empty()) return result;
Permutation(str, result, 0);
//将结果按字典序列重排一下
sort(result.begin(),result.end());
return result;
}
void Permutation(string str, vector<string> &result, int begin){
//终止条件
if(begin == str.size() - 1){
if(find(result.begin(), result.end(), str) == result.end()){
//只有str不在result中才继续添加,避免重复
result.push_back(str);
}
}else{
//第一次循环i与begin相等,相当于第一个位置和自身交换
//之后 i!=begin,则会交换两个不同位置上的字符,直到
//begin = str.size()-1,开始输出
for(int i = begin; i < str.size(); i++){
//轮流去作为第一个元素
swap(str[i], str[begin]);
Permutation(str, result, begin+1);
//复位,用以恢复之前字符串的顺序,达到第一位依次与其它位交换的目的
swap(str[i],str[begin]);
}
}
}
void swap(char &a, char &b){
char tmp = a;
a = b;
b = tmp;
}
};