【LeetCode刷题week3】——字符串专题

LC 字符串-String专题

[声明]:ACWing Y总课程 总结

1.题目链接 — LeetCode38. 外观数列

在这里插入图片描述
代码如下

class Solution {
public:
    string countAndSay(int n) {
        string s = "1";
        for(int i = 0; i < n-1; i++){
            string ns; // 每轮都设置为空
            for(int j = 0; j < s.size(); j++){
                int k = j;
                while(k < s.size() && s[k] == s[j]) k++; // k遍历连续相同的字符
                // k停止在s[k]!=s[j]的地方
                ns += to_string(k-j) + s[j]; // k-j个s[j]
                j = k-1; // 因为会j++ 所以要-1 
            }
            s = ns;
        }
        return s;
    }
};

2.题目链接 — LeetCode49. 字母异位词分组

输入 / 输出样例

输入: strs = ["eat", "tea", "tan", "ate", "nat", "bat"]
输出: [["bat"],["nat","tan"],["ate","eat","tea"]]

代码

class Solution {
public:
    vector<vector<string>> groupAnagrams(vector<string>& strs) {
        unordered_map<string, vector<string>> hash;
        // first记录排好序的同字符单词, second记录同字符单词的源单词
        for(auto s : strs){
            string key = s; // key为排序后的单词
            sort(key.begin(), key.end());
            hash[key].push_back(s); // key:aet  str:eat
        }
        vector<vector<string>> ans;
        for(auto it : hash){
            ans.push_back(it.second);
        }
        return ans;
    }
};

3.题目链接 — LeetCode151. 翻转字符串里的单词

输入 / 输出样例

输入:s = "  hello world  "
输出:"world hello"

在这里插入图片描述
方法一:翻转两次

class Solution {
public:
    string reverseWords(string s) {
        int k = 0; // k 为反转每一个单词时的index
        for(int i = 0; i < s.size(); i++){
            while(i < s.size() && s[i] == ' ') i++; // i 为除空格单词的首字母
            if(i == s.size()) break; // 读到末尾 break
            int j = i; // j为单词末尾
            while(j < s.size() && s[j] != ' ') j++;
            reverse(s.begin()+i ,s.begin()+j);
            if(k != 0) s[k++] = ' '; // 非首单词反转后 + 一个空格
            while(i < j) s[k++] = s[i++];
        }
        s.erase(s.begin() + k, s.end()); //前 k 个才是每个单词反转的结果,
        // 所以把 k 之后的东西删掉
        reverse(s.begin(), s.end()); // 最后把前 k 个反转单词再反转
        return s;
    }
};

方法二:处理多余空格(技巧:stringstream

  • 优点:方便简单,不需要额外处理多余空格
  • 缺点:输入流缓存占内存比较大
class Solution {
public:
    string reverseWords(string s) { // example: __boy___good__a_   
        stringstream ss;
        ss << s; // 输入流
        string t, ans;
        while(ss >> t){ // t 为当前去除空格的单词
            ans = t + " " + ans;
            // boy --> good boy --> a good boy
        }
        ans.erase(ans.begin() + ans.size() - 1); // 去掉末尾最后的一个空格
        return ans;
    }
};

4.题目链接 — LeetCode165. 比较版本号

输入 / 输出样例

输入:version1 = "1.01", version2 = "1.001"
输出:0
解释:忽略前导零,"01""001" 都表示相同的整数 "1"
-------------------------------------------
输入:version1 = "0.1", version2 = "1.1"
输出:-1
解释:version1 中下标为 0 的修订号是 "0",version2 中下标为 0 的修订号是 "1" 
	  0 < 1,所以 version1 < version2
---------------------------------------------
输入:version1 = "1.0", version2 = "1.0.0"
输出:0
解释:version1 没有指定下标为 2 的修订号,即视为 "0"

在这里插入图片描述

代码

class Solution {
public:
    int compareVersion(string v1, string v2) {
        int i = 0, j = 0;
        int a, b;
        while(i < v1.size() || j < v2.size()){
            int x = i, y = j; // 用 i~x 和 j~y 来遍历数字部分
            while(x < v1.size() && v1[x] != '.') x++;
            while(y < v2.size() && v2[y] != '.') y++;
            if(x == i) a = 0; // x==i说明v1没有数字部分了,后面补0
            else a = stoi(v1.substr(i, x-i));  // 截取i开始x-i长度的字符串,并转为int
            if(y == j) b = 0;
            else b = stoi(v2.substr(j, y-j));
            if(a > b) return 1;
            if(a < b) return -1;
            i = x + 1;
            j = y + 1;
        }
        return 0; // 相等
    }
};

5.题目链接 — LeetCode929. 独特的电子邮件地址

在这里插入图片描述
代码1

class Solution {
public:
    int numUniqueEmails(vector<string>& emails) {
        unordered_set<string> hash;

        for(auto &e : emails){
            string loc;
            int n = e.size();
            // 构造 local_name
            for(int i = 0; i < n; i++){
                if(e[i] == '.') continue;
                if(e[i] == '@' || e[i] == '+' ) break;
                loc += e[i];
            }
            // 构造 new DNS
            for(int i = 0; i < n; i++){
                if(e[i] != '@') continue;
                loc = loc + e.substr(i, n-1);
            }
            // cout<<loc<<endl;
            hash.insert(loc);
        }
        return hash.size();
    }
};

代码2:yxc

class Solution {
public:
    int numUniqueEmails(vector<string>& emails) {
        unordered_set<string> hash;
        for(auto &e : emails){
            string loc;
            int at = e.find('@');
            for(auto c : e.substr(0, at)){
                if(c == '+') break;
                else if(c != '.') loc += c;
            }
            string domain = e.substr(at + 1); // 截取 at + 1 到末尾
            hash.insert(loc + '@' + domain);
        }
        return hash.size();
    }
};

6.题目链接 — LeetCode5.最长回文子串

样例

输入:s = "babad"
输出:"bab"
解释:"aba" 同样是符合题意的答案。

方法一:中心扩散法。判断回文串长度,取max
在这里插入图片描述
代码

class Solution {
public:
    string longestPalindrome(string s) {
       string ans;
       for(int i = 0; i < s.size(); i++) {
            // case 1: 遍历子回文串奇数情况
           for(int j = i, k = i; j >= 0 && k < s.size() && s[j] == s[k]; j--, k++) {
               if(ans.size() < k-j+1) ans = s.substr(j, k-j+1);
           } // case 2: 遍历偶数情况, k从i+1开始
           for(int j = i, k = i+1; j >= 0 && k < s.size() && s[j] == s[k]; j--, k++) {
               if(ans.size() < k-j+1) ans = s.substr(j, k-j+1);
           }
       }
       return ans;  
    }
};

7.题目链接 — LeetCode6.Z字形变换

样例
在这里插入图片描述
在这里插入图片描述
代码

class Solution {
public:
    string convert(string s, int n) {
        if(n == 1) return s;
        string ans;
        for(int i = 0; i < n; i++){
            if(!i || i == n-1){ // 处理首尾
                for(int j = i; j < s.size(); j += 2*(n-1)){
                    ans += s[j];
                }
            }
            else{ // 处理中间部分(两个等差数列) 
            	// 数列1:首项i, 数列2:首项 2(n-1)-i. 因为i+k = 2(n-1)
                for(int j = i, k = 2*(n-1)-i; j<s.size() || k<s.size(); j += 2*(n-1), k+=2*(n-1)){
                    if(j < s.size()) ans += s[j];
                    if(k < s.size()) ans += s[k];
                }
            }
        }
        return ans;
    }
};

8.题目链接 — LeetCode3. 无重复字符的最长子串

样例

输入: s = "abcabcbb"
输出: 3 
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3

思路:双指针算法 O(n)

  • 定义两个指针 i,j(j <= i)当前扫描的子串为[ i, j ]
  1. 指针 i 后移,并把哈希表中 s[i] 的计数+1
  2. 假设 i 移动的区间[j, i]中 没有重复字符,则 i 移动后,若 s[i] 出现2次,则说明已经有重复了。因此不断后移 j,直到区间 [i, j] 中 s[i]的个数 = 1

代码

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        unordered_map<char, int> mp;
        int ans = 0;
        int n = s.size();
        for(int i = 0, j = 0; i < n; i++){
            mp[s[i]] ++ ;
            while(mp[s[i]] > 1){ // 如果mp[s[i]] > 1, 说明有重复
                mp[s[j]] --;
                j++;  // j指针不断后移, 直到mp[s[i]]只出现一次
            }
            ans = max(ans, i-j+1);
        }
        return ans;
    }
};

【补充】:Trie 树

补充题目链接:AcWing 835. Trie字符串统计

Trie树,图源ACWing: 四谷夕雨
在这里插入图片描述
【注】这里的son用的是二维数组

定义变量

int son[N][26]; // son存放的:子节点对应的idx。
// ★ son数组:
// [一维]:父节点的idx 
// [二维]:元素值(a:0,b:1,c:2....)。
int cnt [N];    // “abc”中,‘c’对应的idx作为cnt数组的下标。数组的值是idx对应的个数。
int idx;        // 结点编号 每用一个结点idx++
string str;     // 待处理的字符串

Trie树 插入字符串

void insert(string str){
    int p = 0;  // p 为当前节点的idx
    for (int i = 0; str[i]; i++){
        int u = str[i] - '0';
        if (!son[p][u]) son[p][u] = ++idx; // 若p的子结点不存在:创建新结点
        p = son[p][u]; // p 指向 p的子结点
    }
    // 循环结束,此时p就是str中最后一个字符对应的trie树的位置idx
    cnt[p]++;
}

Trie树 查询字符串

// 返回字符串在Trie树的个数
int query(string str){
    int p = 0;  // p 为当前节点的idx
    for (int i = 0; str[i]; i++) {
        int u = str[i] - '0';
        if (!son[p][u]) return 0; // 如果不存在当前字符的子结点,说明不存在这个字符串
        p = son[p][u];            // 沿着Trie树遍历 p -> p.child
    }
    // 循环结束,此时p就是str中最后一个字符对应的trie树的位置idx
    return cnt[p];

9.题目链接 — LeetCode208. 实现 Trie (前缀树)

在这里插入图片描述
定义结构体

struct Node{
        bool tag; // 结尾标记
        Node *son[26]; // 存储字符 son:Node指针变量
        Node(){  // 结构体的构造函数
            tag = false;
            for(int i = 0; i < 26; i++) son[i] = NULL;
        }
    }*root; // 定义root为结构体指针
    
Trie() {
    root = new Node();
}

1 插入字符串

void insert(string word) {
    auto p = root;
    for(auto c : word){
        int u = c - 'a';
        if(p->son[u] == NULL) p->son[u] = new Node(); // 若不存在:创建子结点
        p = p->son[u]; // p指向子结点
    }
    p->tag = true;
}

2 查询字符串是否存在

bool search(string word) {
    auto p = root;
	for(auto c : word){
	    int u = c - 'a';
	    if(p->son[u] == NULL) return false;
	    p = p->son[u];
	}
	return p->tag;
}

3 查询前缀是否存在

bool startsWith(string prefix) {
    auto p = root;
    for(auto c : prefix){
        int u = c - 'a';
        if(p->son[u] == NULL) return false;
        p = p->son[u];
    }
    return true;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值