算法与数据结构学习2-字符串

字符串

  1. 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
			}
		}
	}
};
  1. 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;//如果结果为空,初始时并不为空,则要返回根目录"/”
	}
}		
  1. 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;
    }
};
  1. 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;
	 }
};
  1. 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;
    }
};
  1. 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";
    }
};
  1. 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;
	}
};
  1. 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算法能大大减少字符串查找的复杂度,很好的思想,仔细体会

  1. 字符串的排列
    题目描述
    输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串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;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值