LintCode 解题记录 17.8.7 字符串处理3

来源:互联网 发布:网络课程培训心得 编辑:程序博客网 时间:2024/05/17 22:32

LintCode Generate Parentheses

给定n对括号,写一个函数去产生所有符合要求的括号组合。
思路
递归+回溯。
如何判断一个序号组合序列是有效的呢?我最先想到的是堆栈。左括号就入栈,右括号就出栈(前提栈不为空),如果空了就说明此时的组合不是合法的,就结束此次搜索。
还有一种简单的方法。针对从1~2n的序列,我们发现如下规则始终成立:左括号的个数大于等于右括号的个数。那么用两个指针leftNum和rightNum来表示左括号和右括号的余下的个数。如果leftNum > 0,那么此时添加一个左括号可以;如果 rightNum > leftNum && rightNum > 0,才可以添加一个右括号。如果leftNum=rightNum=0,说明搜索结束,此时产生的序列就是符合题意的。
代码

/**** version 1 ****/    void dfs(vector<string> &res, string tmp, int cnt, stack<int> s, int n) {        if (cnt == 2*n) {            if (s.empty()) res.push_back(tmp);            return;        }        for (int i = 0; i < 2; i++) {            if (i == 0) {                s.push(cnt);                dfs(res, tmp + '(', cnt+1, s, n);                s.pop();            } else {                if (s.empty()) break;                s.pop();                dfs(res, tmp + ')', cnt+1, s, n);                s.push(cnt);            }        }    }    vector<string> generateParenthesis(int n) {        // Write your code here        vector<string> res;        stack<int> s;        string tmp = "";        dfs(res, tmp, 0, s, n);        return res;    }  /**** version 2 **** better version ****/      vector<string> generateParenthesis(int n) {        // Write your code here        int leftNum, rightNum;        leftNum = rightNum = n;        vector<string> res;        string tmp = "";        generate(res, tmp, leftNum, rightNum);        return res;    }    void generate(vector<string> &res, string tmp, int leftNum, int rightNum) {        if (leftNum == 0 && rightNum == 0) {            res.push_back(tmp);            return;        }        if (leftNum > 0) {            generate(res, tmp+'(', leftNum-1, rightNum);        }        if (rightNum > leftNum) {            generate(res, tmp+')', leftNum, rightNum-1);        }    }

LintCode Integer to Roman

把一个数字转成罗马数字的写法。该数字大小不会超过3999。
思路
弄清楚罗马数字的写法,这题就很简单了。
代码

    string intToRoman(int n) {        // Write your code here        char c1[][5] = {"I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"};        char c2[][5] = {"X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC"};        char c3[][5] = {"C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM"};        char c4[][5] = {"M", "MM", "MMM"};        string res = "";        int cnt = 0;        while (n) {            if (n >= 1000) {                res += c4[n/1000-1];                n %= 1000;            } else if (n >= 100) {                res += c3[n/100-1];                n %= 100;            } else if (n >= 10) {                res += c2[n/10-1];                n %= 10;            } else if (n >= 1) {                res += c1[n-1];                break;            }        }        return res;    }

LintCode Letter Combinations of a Phone Number

给定一个数字序列(不包含01),问你可以翻译成多少种字母组合。数字和字母的映射关系就是九宫格输入法。
思路
dfs
代码

    const vector<string> num2letter{"", "", "abc", "def", "ghi", "jkl", "mno", "pqrs",         "tuv", "wxyz"};    vector<string> letterCombinations(string& digits) {        // Write your code here        vector<string> res;        string tmp = "";        int n = digits.size();        if (n) dfs(res, tmp, 0, digits, n);        return res;    }    void dfs(vector<string> &res, string tmp, int idx, string digits, int n) {        if (idx == n) {            res.push_back(tmp);            return;        }        int curr = digits[idx] - '0';        for (int i = 0; i < num2letter[curr].size(); i++) {            tmp += num2letter[curr][i];            dfs(res, tmp, idx+1, digits, n);            tmp.pop_back();        }    }

LintCode Longest Common Prefix

求k个字符串的最长公共前缀
思路
直接暴力搜索就可以。因为LCP一定是每一个字符串的子串。所以就可以选取某一字符串,逐个比较其字符和剩下所有字符串的字符是否相等,相等就跳到下一个字符,如果不相等或者发现一个长度更小的字符串,就返回当前结果。
其实我做的时候我最先想到了Merge K Sorted List这道题,于是也把这道题往这方面想了,当然也能做出来,算是又温习了一下这种问题的分治方法吧。
代码

    string longestCommonPrefix(vector<string> &strs) {        // write your code here        if (strs.size() == 0) return "";        string res = "";        res = findPrefix(strs, 0, strs.size()-1);        return res;    }    string findPrefix(vector<string> &strs, int l, int r) {        if (l == r) return strs[l];        int mid = (l+r) >> 1;        string pre1 = findPrefix(strs, l, mid);        string pre2 = findPrefix(strs, mid+1, r);        string res = findTwoPrefix(pre1, pre2);        return res;    }    string findTwoPrefix(string str1, string str2) {        string ret = "";        for (int i = 0; i < min(str1.size(), str2.size()); i++) {            if (str1[i] == str2[i]) ret += str1[i];            else break;        }        return ret;    }

LintCode Longest Common Substring

求两个字符串的最长公共子串。同理最长公共子序列。
正如题中提到了substring differs with subsequences.
思路
动态规划来解决。我们考虑状态dp[i][j]来表示字符串1的前i个字符和字符串2的前j个字符的最长公共子串(包含第i个字符和第j个字符)。其实这道题的思路有点像最大子序列和。接下来考虑str1[i-1]和str2[j-1],如果str1[i-1]==str2[j-1],那么dp[i][j] = dp[i-1][j-1]+1,如果不相等那么dp[i][j] = 0。然后我们只要在这个过称中寻找到dp[i][j]的最大值即可。
代码

int longestCommonSubstring(string &A, string &B) {    // write your code here    int m = A.size(), n = B.size();    vector<vector<int> > local(m + 1, vector<int>(n + 1, 0));    int maxx = 0;    for (int i = 1; i < m + 1; i++) {        for (int j = 1; j < n + 1; j++) {            local[i][j] = A[i - 1] == B[j - 1] ? local[i - 1][j - 1] + 1 : 0;            maxx = max(maxx, local[i][j]);        }    }    return maxx;}

同理,我们也来研究一下最大子序列的问题。这道题仍然是动态规划,我们用dp[i][j]来表示字符串1的前i位和字符串2的前j位的最大子序列长度。同样考虑str1[i-1]是否和str2[j-1]相等。如果相等,那么分别加上这一位一定形成了更长的子序列,所以此时dp[i][j] = dp[i-1][j-1]+1。如果不想等,那么思考从哪几个子状态中可以一步跳转到该状态呢?于是我们得到如下递推 dp[i][j] = max(dp[i-1][j], dp[i][j-1]);

LintCode Longest Palindromic Substring

求给定字符串的最长回文子串
思路
O(n2)的方法
从回文串的中心向两边遍历来寻找最长回文串,注意回文串有偶数长度的也有奇数长度的。所以对于每一位都要考虑这个因素。
代码

    string longestPalindrome(string s) {        // write your code here        if (s.size() == 0) return "";        int l = 0, r = 0;        int startIdx = 0, len = 0;        for (int i = 0; i < s.size(); i++) {            if (i < s.size()-1 && s[i] == s[i+1]) { //偶数长度                l = i;                r = i+1;                SearchPalindrome(s, l, r, startIdx, len);            }            l = r = i; //奇数长度            SearchPalindrome(s, l, r, startIdx, len);        }        return s.substr(startIdx, len);    }    void SearchPalindrome(string s, int left, int right, int &startIdx, int &len) {        int step = 1;        bool tag = true;        while (left-step >= 0 && right+step < s.size()) {            if (s[left-step] == s[right+step]) {                step++;            } else {                tag = false;                if (right-left+2*step-1 > len) {                    len = right-left+2*step-1;                    startIdx = left-step+1;                }                break;            }        }        if (tag) {                if (right-left+2*step-1 > len) {                    len = right-left+2*step-1;                    startIdx = left-step+1;                }        }    }

思路2
此题也可以用动态规划的方法来解决。维护变量dp[i][j]表示字符串从i到j之间是否为回文串。那么如果i == j,意思就是单个字符,肯定是回文串。如果i和j之间相差为1,意思就是两个字符,就判断这两个字符是否相等来决定是否为回文串。如果i和j之间相差为2,那么dp[i][j] = dp[i+1][j-1] && s[i] == s[j]。
代码就不写了,提及此思路的原因就是想多跟动态规划套套近乎:D

思路3
O(n)的解法,是马拉车算法Manachers’ Algorithm。
算法介绍参考博客 :http://www.cnblogs.com/grandyang/p/4475985.html
代码

    string longestPalindrome(string s) {        string t ="$#";        for (int i = 0; i < s.size(); ++i) {            t += s[i];            t += '#';        }        int p[t.size()] = {0}, id = 0, mx = 0, resId = 0, resMx = 0;        for (int i = 0; i < t.size(); ++i) {            p[i] = mx > i ? min(p[2 * id - i], mx - i) : 1;            while (t[i + p[i]] == t[i - p[i]]) ++p[i];            if (mx < i + p[i]) {                mx = i + p[i];                id = i;            }            if (resMx < p[i]) {                resMx = p[i];                resId = i;            }        }        return s.substr((resId - resMx) / 2, resMx - 1);    }};
原创粉丝点击