Leetcode标签式练习(字符串)

L3 无重复字符的最长子串

  • 子串是连续的
  • 无重复使用无序set记录
class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        unordered_set<char> us;
        int left = 0, right = 0;
        int out = 0;
        while (right < s.size()) {
            us.emplace(s[right++]);
            out = max(out, right - left);
            while (!us.empty() && us.count(s[right]))
                us.erase(s[left++]);
        }
        return out;
    }
};

L5 最长回文子串

  • 子串:连续的
  • 回文
  • dp[i][j]表示i~j是不是回文
  • 遍历次序先从字长1~N遍历!
class Solution {
public:
    string longestPalindrome(string s) {
        int N = s.size();
        if (N < 2) return s;
        vector<vector<bool>> dp(N, vector<bool>(N, false));
        int start = 0, len = 0;
        for (int i = 1; i <= N; i++) {
            for (int j = 0; j <= N - i; j++) {
                int p = j + i - 1;
                dp[j][p] = (s[j] == s[p]) && (p - j < 2 || dp[j+1][p-1]);
                if (dp[j][p] && i > len) {
                    start = j; len = i;
                }
            }
        }
        return s.substr(start, len);
    }
};

L6 Z字形变换

在这里插入图片描述

  • 按区间 0~2numRows-2变换(繁琐)
class Solution {
public:
    string convert(string s, int numRows) {
        int N = 2 * numRows - 2;
        int LEN = s.size();
        if (LEN <= numRows || numRows == 1) return s;
        int i = 0;
        string out[numRows];
        for (i = 0; i < LEN; i += N) {
            int j = i + N - 1;
            for (int k = i; k <= min(j, LEN-1); k++) {
                if (k <= i + numRows - 1) {
                    out[k-i].push_back(s[k]);
                } else {
                    out[N - k + i].push_back(s[k]);
                }
            }
        }
        string res;
        for (int i = 0; i < numRows; i++) 
            res += out[i];
        return res;
    }
};
  • 设一个标志位确定下行还是上行
class Solution {
public:
    string convert(string s, int numRows) {
        int n = s.size();
        if (n < 1 || numRows == 1) return s;
        string out[min(n, numRows)];
        bool dir = false;
        int idx = 0;
        for (auto& c : s) {
            out[idx] += c;
            if (idx == 0 || idx == numRows - 1)
                dir = !dir;
            idx += dir ? 1 : -1;
        }
        string res = "";
        for (auto& ss : out)
            res += ss;
        return res;
    }
};

L8 字符串转换整数 (atoi)

  • 一串字符串转换为整数
  • 如果第一个非空字符为正或者负号时,则将该符号与之后面尽可能多的连续数字字符组合起来,形成一个有符号整数。
  • 假如第一个非空字符是数字,则直接将其与之后连续的数字字符组合起来,形成一个整数。
  • 该字符串在有效的整数部分之后也可能会存在多余的字符,那么这些字符可以被忽略,它们对函数不应该造成影响。
  • “臃肿的代码”
class Solution {
public:
    int myAtoi(string str) {
        if (str.empty()) return 0;
        int idx = 0, idx2 = 0;
        for (idx = 0; idx < str.size(); idx++) {
            if (str[idx] != ' ') break;
        }
        if (idx == str.size()) return 0;
        int sgn = (str[idx] == '-') ? -1 : 
            (isdigit(str[idx]) || str[idx] == '+') ? 1 : -2;
        if (sgn == -2) return 0;
        for (idx2 = idx + 1; idx2 < str.size(); idx2++) {
            if (!isdigit(str[idx2]))
                break;
        }
        string num = str.substr(idx, idx2 - idx);
        long long x = 0;
        int idx3 = idx;
        for (idx3 = idx; idx3 < idx2; idx3++) 
            if (isdigit(str[idx3])) break;
        for (int i = idx3; i < idx2; i++) {
            if (sgn * x * 10 > INT_MAX) return INT_MAX;
            else if ( sgn *x * 10 < INT_MIN) return INT_MIN;
            x = x * 10 + str[i] - '0';
        }
        x *= sgn;
        if (x > INT_MAX) return INT_MAX;
        else if ( x < INT_MIN) return INT_MIN;
        else return x;
    }
};

确定有限状态机(deterministic finite automaton, DFA)

在这里插入图片描述

X’ ’ 0+/- 1number 2other 3
start 0startsignedin_numberend
signed 1endendin_numberend
in_number 2endendin_numberend
end 3endendendend
class Solution {
private:
    class DFA {
    private:
        int state_move[4][4] {{0,1,2,3},{3,3,2,3},
        			{3,3,2,3},{3,3,3,3}};
        int sgn = 1;
        long long num = 0;
        int state = 0;
    public:
        int get_move(char c) {
            if (isspace(c)) return 0;
            else if (c == '+' || c == '-') return 1;
            else if (isdigit(c)) return 2;
            else return 3;
        }
        void run(char c) {
            state = state_move[state][get_move(c)];
            if (state == 1) sgn = (c == '-') ? -1 : 1;
            else if (state == 2) {
                num = num * 10 + c - '0';
                if (sgn == 1) num = min(num, (long long)INT_MAX);
                else num = min(num, -1 * (long long)INT_MIN);
            }
        }
        int get_num() {
            return num * sgn;
        }
    };
public:
    int myAtoi(string str) {
        DFA d;
        for (auto& c : str) d.run(c);
        return d.get_num();
    }
};

L12 整数转罗马数字

  • 输入一个整数,转换为罗马表示形式
  • 贪心 + 列表 从大数表示
class Solution {
private:
    string L[13] {"I", "IV", "V", "IX", "X", "XL", "L", 
        "XC", "C", "CD", "D", "CM", "M"};
    int N[13] {1, 4, 5, 9, 10, 40, 50, 
        90, 100, 400, 500, 900, 1000};
public:
    string intToRoman(int num) {
        if (num == 0) return "N";
        string out = "";
        for (int i = 12; i >= 0; i--) {
            int a = num / N[i];
            if (a == 0) continue;
            for (int j = 0; j < a; j++) 
                out += L[i];
            num = num % N[i];
        }
        return out;
    }
};

L13 罗马数字转整数

  • 13元素表方法
class Solution {
private:
    unordered_map<string, int> R {{"M", 1000}, {"CM", 900}, 
        {"D", 500}, {"CD", 400}, {"C", 100}, {"XC", 90}, {"L", 50}, 
        {"XL", 40}, {"X", 10}, {"IX", 9}, {"V", 5}, {"IV", 4}, {"I", 1}};
public:
    int romanToInt(string s) {
        if (s.empty()) return 0;
        if (s.size() == 1) {
            if (R.count(s) == 0) return 0;
            else return R[s];
        }
        int res = 0;
        s.push_back('N');
        for (int i = 0; i < s.size() - 1;) {
            string a = s.substr(i, 1);
            string b = s.substr(i, 2);
            int na = 0, nb = 0;
            if (R.count(a)) na = R[a];
            if (R.count(b)) nb = R[b];
            if (na == 0 && nb == 0) return 0;
            if (nb > na) {res += nb; i+=2;}
            else {res += na; i++;}
        }
        return res;
    }
};
  • 7元素表方法
class Solution {
private:
    unordered_map<char, int> L {{'I', 1}, 
            {'V', 5}, {'X', 10}, {'L', 50},
            {'C', 100}, {'D', 500}, {'M', 1000}};
    int getNum(char x) {
        switch (x) {
            case 'I' : return 1;
            case 'V' : return 5;
            case 'X' : return 10;
            case 'L' : return 50;
            case 'C' : return 100;
            case 'D' : return 500;
            case 'M' : return 1000;
            default : return 0;
        }
    }
public:
    int romanToInt(string s) {
        // // if (s.size() == 0) return 0;
        // // if (s.size() == 1) {
        // //     if (L.count(s[0])) return L[s[0]];
        // //     else return 0;
        // // }
        // int out = 0;
        // for (int i = 0; i < s.size() - 1; i++) {
        //     char c = s[i], c2 = s[i + 1];
        //     if (L.count(c) == 0 || L.count(c2) == 0) 
        //         return 0;
        //     if (L[c] < L[c2]) out -= L[c];
        //     else out += L[c];
        // }
        // out += L[s.back()];

        int out = 0;
        for (int i = 0; i < s.size() - 1; i++) {
            int n1 = getNum(s[i]), n2 = getNum(s[i + 1]);
            if (n1 == 0 && n2 == 0) return 0;
            if (n1 < n2) out -= n1;
            else out += n1;
        }
        out += getNum(s.back());
        return out;
    }
};

L14 最长公共前缀

  • 编写一个函数来查找字符串数组中的最长公共前缀。
  • 如果不存在公共前缀,返回空字符串 “”
class Solution {
public:
    string longestCommonPrefix(vector<string>& strs) {
        if(strs.empty()) return "";
        const auto p = minmax_element(strs.begin(), strs.end());
        for(int i = 0; i < p.first->size(); ++i)
            if(p.first->at(i) != p.second->at(i)) return p.first->substr(0, i);
        return *p.first;
    }
};

L38 外观数列

  • 「外观数列」是一个整数序列,从数字 1 开始,序列中的每一项都是对前一项的描述。前五项如下:
1.1
2.11
3.21
4.1211
5.111221
  • 模拟
class Solution {
public:
    string countAndSay(int n) {
        if (n < 2) return to_string(n);
        string out = "1";
        while (n > 1) {
            int cnt = 1;
            string str = "";
            for (int i = 0; i < out.size(); i++) {
                char x = out[i];
                if (i + 1 < out.size() && out[i + 1] == out[i])
                    cnt++;
                else {
                    str += to_string(cnt);
                    str.push_back(x);
                    cnt = 1;
                }
            }
            n--;
            out = str;
        }
        return out;
    }
};

L30 串联所有单词的子串

  • 字符串 s 和 长度相同的单词集合words
  • 从s中找到可以由集合组合成的字符子串 的起始位置
  • 暴力搜素,用哈希表记录每个word的个数
class Solution {
public:
    vector<int> findSubstring(string s, vector<string>& words) {
        if (s.empty() || words.empty()) return {};
        int N1 = s.size(), N2 = words.size(), N3 = words[0].size();
        if (N1 < N2 * N3) return {};
        vector<int> out;
        unordered_map<string, int> m1, m2;
        string buff;
        for (auto& w : words) m1[w]++;
        for (int i = 0; i < N1; i++) {
            if (N1 - i + 1 < N2 * N3) break;
            m2 = m1;
            buff = s.substr(i, N3);
            int j = i, count = 0;
            while (m2[buff] > 0) {
                m2[buff]--;
                count++;
                j += N3;
                buff = s.substr(j, N3);
            }
            if (count == N2) 
                out.push_back(i);
        }
        return out;
    }
};
  • 双指针滑动搜素
class Solution {
public:
    vector<int> findSubstring(string s, vector<string>& words) {
        if (words.size() == 0) return {};
        unordered_map<string, int> m;
        for (auto& w : words) m[w]++;
        int word_len = words[0].size();
        vector<int> out;
        for (int i = 0; i < word_len; i++) { // 窗口起点设为 字长个
            int left = i, right = i;
            int cnt = 0;
            unordered_map<string, int> win;
            while (left + words.size() * word_len <= s.size()) {
                string tmp = "";
                while (cnt < words.size()) { // 右移
                    tmp = s.substr(right, word_len);
                    if (m.count(tmp) == 0 || win[tmp] >= m[tmp])
                        break;
                    win[tmp]++;
                    cnt++;
                    right += word_len;
                }
                if (win == m) out.push_back(left);
                if (m.count(tmp)) { // 判断如何左移
                    win[s.substr(left, word_len)]--;
                    left += word_len; // 左移一位
                    cnt--;
                } else {
                    right += word_len;
                    left = right; // 左移一整个子串长
                    cnt = 0;
                    win.clear();
                }
            }
        }
        return out;
    }
};

L43 字符串相乘

  • 字符串表示的两个非负整数,返回其乘积
  • 不能使用任何标准库的大数类型(比如 BigInteger)或直接将输入转换为整数来处理。
  • i+j的位置就是输出字符串的基本位置(存在进位)
class Solution {
public:
    string multiply(string num1, string num2) {
        int sz1 = num1.size();
        int sz2 = num2.size();
        if (sz1 == 0) return num2;
        if (sz2 == 0) return num1;
        string res = "";
        res.resize(sz1 + sz2, '0');
        // 1 按位累积
        for (int i = 1; i <= sz1; i++) {
            for (int j = 1; j <= sz2; j++) {
                int a = num1[i-1] - '0';
                int b = num2[j-1] - '0';
                int c = res[i+j-1] - '0';
                int tmp = a * b + c;
                res[i+j-1] = tmp % 10 + '0';
                res[i+j-2] += tmp / 10;
            }
        }
        // 2 解决部分位大于'9'的情况 "297<24"
        for (int i = res.size() - 1; i > 0; i--) {
            int tmp = res[i] - '0';
            res[i] = tmp % 10 + '0';
            res[i-1] += tmp / 10;
        }
        // 3 解决头元素为'0'
        int start = 0;
        for (int i = 0; i < res.size(); i++) {
            if (res[i] != '0') return res.substr(i);
        }
        return "0";
    }
};
  • 上面的第二步可以不需要,如果第一步是从低位累积起来的
class Solution {
public:
    string multiply(string num1, string num2) {
        int sz1 = num1.size();
        int sz2 = num2.size();
        if (sz1 == 0) return num2;
        if (sz2 == 0) return num1;
        string res = "";
        res.resize(sz1 + sz2, '0');
        // 1 按位累积
        for (int i = sz1; i >= 1; i--) {
            for (int j = sz2; j >= 1; j--) {
                int a = num1[i-1] - '0';
                int b = num2[j-1] - '0';
                int c = res[i+j-1] - '0';
                int tmp = a * b + c;
                res[i+j-1] = tmp % 10 + '0';
                res[i+j-2] += tmp / 10;
            }
        }
        // // 2 解决部分位大于'9'的情况 "297<24"
        // for (int i = res.size() - 1; i > 0; i--) {
        //     int tmp = res[i] - '0';
        //     res[i] = tmp % 10 + '0';
        //     res[i-1] += tmp / 10;
        // }
        // 3 解决头元素为'0'
        int start = 0;
        for (int i = 0; i < res.size(); i++) {
            if (res[i] != '0') return res.substr(i);
        }
        return "0";
    }
};

L65 有效数字

  • 输入一串字符串,判断能不能转换为数字
    在这里插入图片描述
  • 只考虑有效的状态之间的转移,这样画图会简单一点
  • 直接失效的直接设为-1,不用进行转移过程了!
class DFA {
private:
    int state_move[9][6] {
        {0,1,2,-1,6,-1},
        {-1,-1,2,-1,6,-1},
        {-1,-1,-1,-1,3,-1},
        {8,-1,-1,4,3,-1},
        {-1,7,-1,-1,5,-1},
        {8,-1,-1,-1,5,-1},
        {8,-1,3,4,6,-1},
        {-1,-1,-1,-1,5,-1},
        {8,-1,-1,-1,-1,-1}};
    int valid_state[4] {3,5,6,8};
    int state = 0;
public:
    int get_move(char c) {
        if (isspace(c)) return 0;
        else if (c == '+' || c == '-') return 1;
        else if (c == '.') return 2;
        else if (c == 'e') return 3;
        else if (isdigit(c)) return 4;
        else return 5;
    }
    void run(char c) {
        if (state >= 0)
            state = state_move[state][get_move(c)];
    }
    int get_state() {
        return state;
    }
    bool isValid() {
        for (auto& s : valid_state)
            if (state == s) return true;
        return false;
    }
};
class Solution {
public:
    bool isNumber(string s) {
        DFA d;
        for (auto& c : s)
            d.run(c);
        return d.isValid();
    }
};

L93 复原IP地址

输入: “25525511135”
输出: [“255.255.11.135”, “255.255.111.35”]

  • DFS方法
class Solution {
    vector<string> res;
    vector<int> segs;
    int LEN = 4;
public:
    vector<string> restoreIpAddresses(string s) {
        segs.resize(LEN, 0);
        dfs(s, 0, 0);
        return res;
    }
    void dfs(string& s, int idx, int pos) {
        if (idx == LEN && pos == s.size()) {
            string tmp = "";
            for (int i = 0; i < LEN; i++) {
                tmp += to_string(segs[i]);
                tmp += ".";
            }
            tmp.pop_back();
            res.push_back(tmp);
            return;
        }
        if (idx == LEN || pos == s.size()) return;
        if (s[pos] == '0') { // 0的只能有一种情况
            segs[idx] = 0;
            dfs(s, idx+1, pos+1);
        }
        int num = 0;
        for (int i = pos; i < s.size(); i++) {
            num = num * 10 + (s[i] - '0');
            if (num > 0 && num <= 255) {
                segs[idx] = num;
                dfs(s, idx + 1, i + 1);
            } else break;
        }
    }
};
  • 迭代for 三层循环
class Solution {
public:
    vector<string> restoreIpAddresses(string s) {
        vector<string> res;
        int n = s.size();
        if (n > 12) return {};
        for (int i = 0; i < min(3, n - 3); i++) {
            for (int j = i + 1; j < min(j + 3, n - 2); j++) {
                for (int k = j + 1; k < min(k + 3, n - 1); k++) {
                    if (n - k > 4) continue;
                    string s1 = s.substr(0, i + 1);
                    if (s1[0] == '0' && s1 != "0") continue;
                    string s2 = s.substr(i + 1, j - i);
                    if (s2[0] == '0' && s2 != "0") continue;
                    string s3 = s.substr(j + 1, k - j);
                    if (s3[0] == '0' && s3 != "0") continue;
                    string s4 = s.substr(k + 1);
                    if (s4[0] == '0' && s4 != "0") continue;
                    long n1 = stol(s1), n2 = stol(s2);
                    long n3 = stol(s3), n4 = stol(s4);
                    if (n1 >= 0 && n1 <= 255 && n2 >= 0 && n2 <= 255 
                            && n3 >= 0 && n3 <= 255 && n4 >= 0 && n4 <= 255) {
                        string tmp = s1 + "." + s2 + "." + s3 + "." + s4;
                        res.push_back(tmp);
                    }
                }
            }
        }
        return res;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值