网易2017秋招笔试题3:最长公共子括号序列长度
【问题来源】网传的2017网易秋招笔试题
【问题描述】
【算法思路】
下面的解题思路摘自 http://www.cnblogs.com/Atanisi/p/7500186.html
刚看到题我就想到暴力解,深搜出所有合法的括号序列,再依次比较公共子序列的长度,返回最长的。但是深搜一般和路径有关,这道题仅仅需要最大公共子序列的长度。而我们发现最大公共子序列的长度就是 s.size() - 1(当且仅当修改距离为 1 时 LCS 最大), 那么我们就想到,可以变换 s 中一个括号的位置,枚举所有的最大公共子序列长度的序列,只是需要判断是否合法。注意到,深搜是枚举所有合法的相同长度的序列,然后我们再在这个集合中去最长的。而后一种思路是我们枚举所有最长公共子序列的序列,然后再在这个集合中去掉非法的和重复的。
因为是 s, t 长度相同,并且 t 也是合法的括号序列,所以正反括号数跟原来一样。考虑在原序列上枚举一个字符,把这个插入依次到序列的某个位置去,其他序列相对顺序不变,,这样就可以让LCS最大,然后我们判断一下是否合法(这里有个小技巧),去重就用 set。
【代码】
1 #include <iostream> 2 #include <string> 3 using namespace std; 4 5 int main() { 6 string s; 7 cin >> s; 8 set<string> m; 9 int n = s.size(); 10 for (int i = 0; i < n; ++i) { 11 string w = s.substr(0, i) + s.substr(i + 1); 12 for (int j = 0; j < n - 1; ++j) { 13 string tmp = w.substr(0, j) + s[i] + w.substr(j); 14 int num = 0; 15 for (int k = 0; k < n; ++k) { 16 num += (tmp[k] == '(' ? 1 : -1); 17 if (num < 0) 18 break; 19 } 20 if (num >= 0) 21 m.insert(tmp); 22 } 23 } 24 cout << (int)m.size() - 1 << endl; 25 26 return 0; 27 }
前两天做了京东和网易的前端笔试题目,都是跟括号有关的....
京东的题目是:
最后求方案数,其实这道题本质上不难,但是需要挖掘题意,这道题需要我们逆向考虑,当碰到第一个右括号,其实它左侧的所有括号都可以与之匹配被删掉并且剩下的括号一定是有效的括号,但是每次过滤完一个右括号就要将左边现有剩下的左括号去掉一个
代码:
function getRes(str) { var res = 1, tmp = 0; for (var i = 0; i < str.length; i++) { if (str[i] == '(') { tmp++; } else { res *= tmp; tmp--; } } return res; }
而网易的最后一道编程题是最长公共子括号序列
题解:因为要求的T需要和S不一样,所以T和S的最长公共子括号序列的长度最多为原字符串的长度N减1,而这个N-1可以通过只改变1个字符在S中的位置得到,方法是:遍历S,将每个位置上的字符尝试插入到剩余的字符串的任意位置上(两重循环),如果这个组合后的字符串是个合法的括号序列,那么它和原字符的公共子括号序列长度就是N-1,即最长,最后的结果是set集合大小减1的原因是里面还会包含原字符串
#include <iostream> #include <set> using namespace std; bool isValid(string str) { int tmp = 0, len = str.size(); for (int i = 0; i < len; i++) { tmp += (str[i] == '(') ? 1 : -1; if (tmp < 0) { return false; } } return true; } int main() { string str; cin >> str; set<string> S; int len = str.size(); for (int i = 0; i < len; i++) { string w = str.substr(0, i) + str.substr(i+1); for (int j = 0; j < len - 1; j++) { string u = w.substr(0, j) + str[i] + w.substr(j); if (isValid(u)) { S.insert(u); } } } cout << (int)S.size() - 1 << endl; }
今天就顺势学习了一下LSC算法:
主要思路:动态规划
设arr[i][j]表示第一个字符串s1的前i位与第二个字符串s2的前j位的最长公共子序列
如果i = 0 || j = 0,那么arr[i][j] = 0
如果 s1[i-1]=s2[j-1],那么arr[i][j] = arr[i-1][j-1] + 1(这里为什么是字符串之间i-1的比较但是却更新的是arr[i]呢?因为arr[][]是从1开始计算的,0的位置都是初始数据,但是字符串还是必须从0开始遍历的)
否则:arr[i][j] = Math.max(arr[i - 1][j], arr[i][j - 1]);
代码:
主要有两个功能,一个是求最长公共子串的长度,一个是打印出路径(采取标记法)
var record = []; var str1 = "abcbdab", str2 = "bdcaba"; var len1 = str1.length, len2 = str2.length; var lscString = ""; function getLSCLength() { var arr = []; for (var i = 0; i <= len1; i++) { arr[i] = new Array(len2 + 1); record[i] = new Array(len2 + 1); for (var j = 0; j <= len2; j++) { arr[i][j] = 0; record[i][j] = ""; } } for (var i = 1; i <= len1; i++) { for (var j = 1; j <= len2; j++) { if (str1[i - 1] == str2[j - 1]) { arr[i][j] = arr[i - 1][j - 1] + 1; record[i][j] = 'left_up'; } else { arr[i][j] = Math.max(arr[i - 1][j], arr[i][j - 1]); if (arr[i - 1][j] >= arr[i][j - 1]) { record[i][j] = "up"; } else { record[i][j] = "left"; } } } } return arr[len1][len2]; } function LSC(i, j) { if (i == 0 && j == 0) return; if (record[i][j] == "left_up") { lscString += str1[i - 1]; LSC(i - 1, j - 1); } else if (record[i][j] == "left") { LSC(i, j - 1); } else if (record[i][j] == "up") { LSC(i - 1, j); } } console.log("the longest subsequence length is " + getLSCLength()); LSC(len1, len2); console.log("the longest subsequence is " + lscString);
转载来自:http://www.cnblogs.com/JesusAlone/p/7536149.html
http://www.cnblogs.com/chenyHAHA/p/7501512.html