[leetcode] String类型题目总结

这篇文章总结一下leetcode online judge中有关string的问题。

string的问题中,比较有意思的一类是,确定某个string中符合某些条件的范围。例如,求满足某些条件的最短字串(例如下面的题目1和题目2)。另一类属于经典算法,例如strstr,KMP算法,随便一本算法书上都有,这里就不赘述了。

第一类问题有些一次遍历就可以解决(例如题目1),有些需要一次遍历中带循环(例如题目2)。无论哪一类问题,在遍历string的时候,都会涉及char的操作。一般遍历问题如果需要保存中间结果,我们一般会选择key-value类型的容器(例如std::map)。但是char类型的值是有范围的,即[1, 128]。解题过程中,往往可以利用这个性质,简化char类型的查找和计数。例如,记录一个字符串中每个字符的出现次数,不需要使用std::map等key-value容器,使用长度为128的int数组就可以。从map到数组是O(nlogn)到O(n)性能提升,显然能够大幅提高程序性能。

1. Longest Substring Without Repeating Characters

题目描述:

Given a string, find the length of the longest substring without repeating characters. For example, the longest substring without repeating letters for "abcabcbb" is "abc", which the length is 3. For "bbbbb" the longest substring is "b", with the length of 1.

解题思路:

记录上一次出现重复字符的位置begin,遍历string,对每个i,查找[begin, i-1]中是否有字符等于s[i]。如果有s[j] == s[i],则设置begin=j+1。那么当前“无重复字符的字符串”为s[begin, i]。

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        // Start typing your C/C++ solution below
        // DO NOT write int main() function
        if (s.length() == 0) {
            return 0;
        }

        int llen = 1;
        int begin = 0;

        for (int i = 1; i < s.length(); i++) {
            for (int k = i - 1; k >= begin; k--) {
                if (s[k] == s[i]) {
                    begin = k + 1;
                    break;
                }
            }

            int cur_len = i - begin + 1;
            if (cur_len > llen) {
                llen = cur_len;
            }
        }

        return llen;
    }
};

这个算法的复杂度其实不是O(n),而是O(n^2),因为在第一个for循环内,为了查找上一个和当前字符相同的字符的位置,又进行了一次for循环。

让我们回想一下之前的分析,因为char的字符一共才128个,使用int[128]来存储每个字符最后一次出现的位置,那么就不需要每次都重新计算了。改进后的算法如下:

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        // Start typing your C/C++ solution below
        // DO NOT write int main() function
        if (s.length() == 0) {
            return 0;
        }

        int last_pos[128];
        for (int i = 0; i < 128; i++)
            last_pos[i] = -1;

        int llen = 1;
        int begin = 0;

        for (int i = 0; i < s.length(); i++) {
            if (last_pos[s[i]] >= begin)
                begin = last_pos[s[i]] + 1;

            int cur_len = i - begin + 1;
            if (cur_len > llen) {
                llen = cur_len;
            }
            last_pos[s[i]] = i;
        }

        return llen;
    }
};

2. Minimum Window Substring

题目描述:

Given a string S and a string T, find the minimum window in S which will contain all the characters in T in complexity O(n).

For example,
S = "ADOBECODEBANC"
T = "ABC"

Minimum window is "BANC".

Note:
If there is no such window in S that covers all characters in T, return the emtpy string "".
If there are multiple such windows, you are guaranteed that there will always be only one unique minimum window in S.

解题思路:

维护一个pattern数组,记录T中每个字符出现的次数;维护一个text数据,记录S[begin, end]中每次字符出现的次数。如果text中每个字符的出现次数都大于pattern中每个字符出现的次数,则认为找到了一个S的子字符串,包含了T。

基本的思路是,begin和end初始设置为0,从S[begin]开始遍历,增加end,直到S[begin, end] >= pattern为止,此时S[begin, end]已经包含了T,记录下当前的begin和end,但这个begin和end不一定是最小的范围。然后开始增加begin,如果S[begin, end]仍然能包含T,则更新最小的begin和end,直到不能包含T为止。此时,增加end,直到能够包含T为止,重复以上的步骤。

class Solution {
public:
    string minWindow(string S, string T) {
        const size_t CHAR_COUNT = 128;
        int text[CHAR_COUNT];
        int pattern[CHAR_COUNT];

        for (size_t i = 0; i < CHAR_COUNT; i++)
            text[i] = pattern[i] = 0;
        for (size_t i = 0; i < T.length(); i++)
            pattern[T[i]]++;

        int min_begin = -1, min_end = -1;
        int begin = 0, end = 0;

        text[S[end]]++;
        while (end < S.length()) {
            if (greaterOrEqual(text, pattern, CHAR_COUNT)) {
                if (begin == 0 || (end - begin) < (min_end - min_begin)) {
                    min_begin = begin;
                    min_end = end;
                }
                text[S[begin]]--;
                begin++;
            } else {
                end++;
                text[S[end]]++;
            }
        }

        if (min_begin != -1)
            return S.substr(min_begin, min_end - min_begin + 1);
        else
            return string();
    }

    bool greaterOrEqual(int *a1, int *a2, size_t size) {
        for (size_t i = 0; i < size; i++) {
            if (a1[i] < a2[i])
                return false;
        }
        return true;
    }
};

3. Implement strStr()

题目描述:

Implement strStr().


Returns a pointer to the first occurrence of needle in haystack, or null if needle is not part of haystack.

解题思路:

KMP算法,任何一本算法书都会讲,具体不赘述,关键是创建partial match table。


#include "common.h"

class Solution {
public:
    char *strStr(char *haystack, char *needle) {
        // Start typing your C/C++ solution below
        // DO NOT write int main() function
        const int H_LEN = strlen(haystack);
        const int N_LEN = strlen(needle);

        if (N_LEN == 0) return haystack;
        if (H_LEN < N_LEN) return NULL;

        int *table = new int[N_LEN];
        build_partial_match_table(needle, table, N_LEN);

        int hidx = 0;
        int nidx = 0;
        while (hidx + nidx < H_LEN) {
            cout << "hidx=" << hidx << ", nidx=" << nidx << endl;
            if (haystack[hidx+nidx] == needle[nidx]) {
                if (nidx == N_LEN-1) return haystack + hidx;
                nidx++;
            } else {
                // table[0]必须是-1
                // 如果hidx=0,nidx=0,如果table[0]不是-1,则会陷入死循环
                hidx = hidx + nidx - table[nidx];
                if (table[nidx] > -1) nidx = table[nidx];
                else nidx = 0;
            }
        }
        return NULL;
    }

    void build_partial_match_table(const char *str, int *table, size_t len) {
        if (len <= 0) return;
        table[0] = -1;
        if (len == 1) return;
        table[1] = 0;

        size_t pos = 2;
        size_t cnd = 0;    // next character of the current candidate substring

        while (pos < len) {
            if (str[pos-1] == str[cnd]) {
                cnd++;
                table[pos] = cnd;
                pos++;
            } else if (cnd > 0) {
                cnd = table[cnd];
            } else {
                table[pos] = 0;
                pos++;
            }
        }
        return;
    }
};





  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值