牛客网(直通BAT面试算法班) 第三章,字符串,专题系列 Day4

时间:2021-12-16 09:47:48
字符串很难很难,面试题目变化多端,涉及类型较多。要经常刷题 3.2 拓扑结构相同子树练习

对于两棵彼此独立的二叉树A和B,请编写一个高效算法,检查A中是否存在一棵子树与B树的拓扑结构完全相同。

给定两棵二叉树的头结点AB,请返回一个bool值,代表A中是否存在一棵同构于B的子树。
思路1:暴力遍历二叉树:先序遍历A的各个节点的同时再去遍历各个B的节点。以A的根节点为起点,去遍历树B的各个节点,之后再以A的左子树为起点,去遍历树B的各个节点,再遍历的过程中同时进行比较各个节点。两个递归函数。复杂度为O(mn)思路2:序列化二叉树,-》字符串,之后在进行字符串的匹配,KMP算法。A-> StrA   B-> StrB , 在StrA中查找是否存在StrB的子串。复杂度为 O(m+n)代码1:(引用)
/*struct TreeNode {    int val;    struct TreeNode *left;    struct TreeNode *right;    TreeNode(int x) :            val(x), left(NULL), right(NULL) {    }};*/class IdenticalTree {public:    bool chkIdentical(TreeNode* A, TreeNode* B) {        // write code here        bool res=false;        if(A!=NULL)        {            if(res==false&&A->val==B->val)                res=compare(A,B);            if(res==false&&A->left!=NULL)                res=chkIdentical(A->left,B);            if(res==false&&A->right!=NULL)                res=chkIdentical(A->right,B);        }        return res;    }    bool compare(TreeNode* A,TreeNode* B)      {        if(A==NULL&&B==NULL)  //必须判断树的各个节点情况            return true;        else if(A!=NULL&&B==NULL)              return false;        else if(A==NULL&&B!=NULL)            return false;        if(A->val!=B->val)            return false;        return compare(A->left,B->left)&&compare(A->right,B->right);    }};
class IdenticalTree {public:    bool chkIdentical(TreeNode* A, TreeNode* B) {        // write code here        string strA;        string strB;        serializeTree(A,strA);        serializeTree(B,strB);       return strA.find(strB) != string::npos;  //查找    }
    void serializeTree(TreeNode *root,string &B){  //序列化操作     if(root==NULL){       B+="#!";       return;     }       B+=to_string(root->val);       B+="!";      serializeTree(root->left,B);      serializeTree(root->right,B);    }   };

3.3 词语变形词对于两个字符串A和B,如果A和B中出现的字符种类相同且每种字符出现的次数相同,则A和B互为变形词,请设计一个高效算法,检查两给定串是否互为变形词。给定两个字符串AB及他们的长度,请返回一个bool值,代表他们是否互为变形词。基本的思路用内存换时间,设置一个容器来,比如数组(或者字典,map都行)来存放每个字符串里面出现的次数,对每个串进行计数,最后再比较是否相同。Python的例子如下:class Transform:    def chkTransform(self, A, lena, B, lenb):        dictA = self.countDict(A,lena)        dictB = self.countDict(B,lenb)        return dictA == dictB    def countDict(self,A,lena):        dictA = {}        for i in range(lena):            if A[i] not in dictA:                dictA[A[i]] = 0            dictA[A[i]] += 1        return dictA
更胜内存的思路是用位运算,A ^ A = 0 ,让字符串中的每一个字符进行位运算到一个变量中,若变量最后得0,则 A 和 B 每一个字符和数量都相同。 class Transform {public:    bool chkTransform(string A, int lena, string B, int lenb) {        int result=0;        for(int i=0;i<lena;i++){            int temp=A[i]-'0';            result=result^temp;        }        for(int i=0;i<lenb;i++){            int temp=B[i]-'0';            result=result^temp;        }        if(result==0)            return true;        else            return false;        // write code here    }};

3.6 句子的逆序练习题

对于一个字符串,请设计一个算法,只在字符串的单词间做逆序调整,也就是说,字符串由一些由空格分隔的部分组成,你需要将这些部分逆序。

给定一个原字符串A和他的长度,请返回逆序后的字符串。

测试样例:
"dog loves pig",13
返回:"pig loves dog"
水题,但是很考察基本的编程能力。思路:两次翻转字符串,第一次对整个字符串进行翻转,dog loves pig -> gip sevol god接着对每个空格分类的单词进行翻转,得到 pig loves dogAC代码 如下:
class Reverse {public:    string reverseSentence(string A, int n) {        // write code here        int end = n-1;        reverseStr(A,0,end);        int j=0;        for(int i=0;i<=n;++i){            if(A[i]==' '){ //meet the space              reverseStr(A,j,i-1);              j=i+1;            }            if(i==n){ //handle the last output             reverseStr(A,j,i-1);             break;            }        }
         return A;         }
    void reverseStr(string &A,int begin,int end){           while(begin<end){            swap(A[begin],A[end]);            begin++;            end--;        }    }};

参考别人写的代码,中间那块更好,这样不用考虑最后结束的那个单词。
class Reverse {public:    void reverseString(string &A,int begin,int end){        int f=begin,b=end-1;        while(f<b){            swap(A[f],A[b]);            f++;            b--;        }    }    string reverseSentence(string A, int n) {        // write code here        reverseString(A,0,n);        int i=0,start=0;        while(i<n){            while(i<n&&A[i]!=' ')i++;            reverseString(A,start,i);            i++;            start=i;        }        return A;    }};

3.7   字符串移位练习题

对于一个字符串,请设计一个算法,将字符串的长度为len的前缀平移到字符串的最后。

给定一个字符串A和它的长度,同时给定len,请返回平移后的字符串。

测试样例:
"ABCDE",5,3
返回:"DEABC"
很经典的字符串题目: 最好的思路不是一个一个地去移动位置,而是3次翻转。 1, 翻转要移动位置的字符串,比如 ABC,-》CBA2, 翻转位置之后的字符串,DE,-》ED  3, 翻转整体。CBAED-》     DEABC实现比较简单。
class Translation {public:    string stringTranslation(string A, int n, int len) {        // write code here        reverseString(A,0,len);        reverseString(A,len,n);        reverseString(A,0,n);           return A;    }    void reverseString(string &A,int begin,int end){        int f=begin,b=end-1;        while(f<b){            swap(A[f],A[b]);            f++;            b--;        }    }};
3.8 拼接最小字典序练习题

对于一个给定的字符串数组,请找到一种拼接顺序,使所有小字符串拼接成的大字符串是所有可能的拼接中字典序最小的。

给定一个字符串数组strs,同时给定它的大小,请返回拼接成的串。

测试样例:
["abc","de"],2
"abcde"
思路:字符串的字典序,c++ 用> 或< 符号就能进行比较,需要确定的是使得整体的字典序最小。把每个串看成一个词,让每个词与每个词(两个一起枚举)进行比较,把字典序小的放在前面,大的放在后面,最后拼接在一起即可。
class Prior {public:    string findSmallest(vector<string> strs, int n) {        // write code here        string temp;        for(int i=0;i<strs.size();++i){            for(int j=i;j<strs.size();++j){                string s1 (strs[i]+strs[j]);                string s2 (strs[j]+strs[i]);                if(s1>s2){                    swap(strs[i],strs[j]);                }            }        }        for(int i=0;i<strs.size();++i){           temp += strs[i];        }          return temp;    }};
     
3.11 合法括号序列判断练习题

对于一个字符串,请设计一个算法,判断其是否为一个合法的括号串。

给定一个字符串A和它的长度n,请返回一个bool值代表它是否为一个合法的括号串。

测试样例:
"(()())",6

返回:true
测试样例:
"()a()()",7

返回:false
测试样例:
"()(()()",7
返回:false
思路1:用栈来判断,遇到 ( 入栈  遇到 ) 弹栈,遇到既不是左括号 也不是右括号时候, return false如果 字符串没有遍历完时候,遇到栈为空且还有弹栈 return false(右括号太多的情况)遍历完后检查 栈是否为空,若为空,return true ,否则 retu false (左括号太多)
思路2:用一个int 变量 num  去记录 左右括号的数量,若遍历的过程中,num <0 ,右括号太多,直接False否则 遇到左括号 num++,遇到右括号 num--最后在判断 num 是否为0(和栈是否为空一个意思),这个做法更省空间。
class Parenthesis {public:    bool chkParenthesis(string A, int n) {        // write code here        int num =0;        for(int i=0;i<n;i++){             if(A[i]=='(')                {                num ++;                continue;                }             else if(A[i]==')'){                num--;                 continue;                }              else{                  return false;              }             if(num<0)                return false;        }        if(num!=0)           return false;        else            return true;    }
};
3.12  最长无重复字符子串练习题

对于一个字符串,请设计一个高效算法,找到字符串的最长无重复字符的子串长度。

给定一个字符串A及它的长度n,请返回它的最长无重复字符子串长度。保证A中字符全部为小写英文字符,且长度小于等于500。

测试样例:"aabcb",5返回:3
LeetCode 的原题 给出了更多的测试样例:

Given "abcabcbb", the answer is "abc", which the length is 3.

Given "bbbbb", the answer is "b", with the length of 1.

Given "pwwkew", the answer is "wke", with the length of 3. Note that the answer must be asubstring, "pwke" is a subsequence and not a substring.
思路1:用hash 表的思路(查重复用的),暴力去遍历每个子串,并去记录每个无重复串的长度,进行比较和更新。时间复杂度较高O(n^2)
class DistinctSubstring {public:    int longestSubstring(string A, int n) {        // write code here        int ret = 0;        if (A.size() <= 1) {            ret = A.size();        }        set<char> setChars;        for (int i = 0; i < A.size(); ++i) {            for (int j = i; j < A.size(); ++j) {                if (setChars.count(A[j])) { // find the existed charcater                    break;                } else {                    setChars.insert(A[j]); // insert the new character                }            }            if (setChars.size() > ret) {//find the long substring                ret = setChars.size();            }            setChars.clear();        }        return ret;    }};
思路2:dp (动态规划思路)+hash (http://www.jianshu.com/p/aa39fe18272a)动态规划的思路:

首先理解,动态规划算法的思想,将问题分解为子问题的解,找到重叠子问题和最优子结构,对需要重复计算的结果进行存储。在这里最优子结构很好理解,在所有无重复子串中,长度最长的就是最优的。而重叠子问题呢?简单的理解,就是当前的LNRS串可以由上一次计算的LNRS得到。

重叠子问题:我们考虑两种情况。

  1. 当前的字符和前面的字符没有重复,那么到当前字符的最长无重复子串就应该在上次求得的LNRS串长度+1,;
  2. 如果当前的字符有冲突,那么有两种情况需要分析。第一,如果和它冲突的字符在当前最长无重复子串的起始位置前面,那么很明显,对计算当前的LNRS串没有影响,还是在上次基础上+1;如果冲突字符在当前的LNRS串开始位置之后,那么就从后一个位置计算无重复子串的长度。
实现时 用一个map 代替hash表,来存储不重复的子串。
class DistinctSubstring {public:    int longestSubstring(string A, int n) {        // write code here                int temp=0;        int len=1;        if(n==0)            return temp;        map<char,int> m;        for(int i=0;i<n;){           if(m.find(A[i])==m.end()){            m.insert(pair<char,int>(A[i],i));            temp++;            i++;            if(temp>len)            len = temp;           }           else{            int pos= m.find(A[i])->second;            i = pos + 1;            m.clear();             temp = 0;           }    }    return len;    }};