与字符串有关的一些典型问题的C++解法

一、C++中String类的用法总结
二、与字符串相关的典型问题
1、查找一棵二叉树是否是另一棵二叉树的子结构
①普通解法:递归进行比较,O(N*M)
②最优解: 树序列化为字符串 + KMP算法 ,O(N+M)

class   IdenticalTree {
public :
// 先把树序列化成字符串,与常规的二叉树遍历不同,这里遇到空指针就填‘#’是为了使序列唯一化,不然光凭先序序列是无法唯一确定二叉树的结构的。
     void preSerial(TreeNode* tree,string &s) {
         if  (tree == NULL) {
            s += '#';
            return;
        }                      
        s += tree->val;
        preSerial(tree->left,s);
        preSerial(tree->right,s);
    }
// 获取模式串的next数组
    
vector< int > getNext(string &s) {
         int   length = s.size();
         vector< int > next(length);
        next[ 0 ] =  0 ;
         for ( int   i =  1 ,q =  0 ; i < length;i++) {
             while   (q >  0  && s[i] != s[q]) 
                q = next[q-1];
             if   (s[i] == s[q]) 
                q++;
            next[i] = q;
        }
         return   next;
    }
// 用kmp算法进行匹配判断
     bool  kmp(string &s1,string &s2) {
         int   length1 = s1.size(),length2 = s2.size();
         if   (length1 < length2) 
             return false ;
         vector< int > next = getNext(s2);
         for ( int   i =  0 , q=  0 ; i < length1;i++) {
             while   (q >  0  && s1[i] != s2[q]) 
                q = next[q-1];
             if  (s1[i] == s2[q]) 
                q++;
             if   (q == length2) 
                 return  true;
        }
         return  false;
    }

     bool   chkIdentical(TreeNode* A, TreeNode* B) {
        string str1,str2;       
        preSerial(A,str1);
        preSerial(B,str2);
         return   kmp(str1,str2);
    }
};

2、判断两个字符串是否互为变形词
变形词: A和B中出现的字符种类相同且每种字符出现的次数相同。
①利用 hash表来进行词频统计;
②用固定长度的数组来实现 hash表
public :
     bool  chkTransform(string A,  int  lena, string B,  int  lenb) {
         if  (lena != lenb || lena <  0 )
             return  false;
         int  hashTable[ 256 ] = { 0 };
        for ( int  i =  0 ;i < lena;i++) {
             hashTable[A[i]]++;  // 此题无需处理碰撞问题,且字符的ASCII码范围是0~255,所以 哈希函数直接用直接寻址法
             hashTable[B[i]]--;   // hash表长度就是ASCII码的范围。
         }
        for  ( int  j =  0 ;j <  256 ;j++)
             if  (hashTable[j] !=  0
                 return  false;
         return  true;
    }
};

3、判断是否互为旋转词
对于一个字符串A,将A的前面任意一部分挪到后边去形成的字符串称为A的旋转词。
最优解法:将A串追加到A串自己的后面,这样AA串里的子串包含了全部的旋转词,再用kmp算法判断B串是否能与AA串匹配,若能,则B、A互为旋转词,反之则不是。

class Rotation {
public :
     vector< int > getNext(string s) {
         int length = s.size();
         vector< int > next(length);
         next[ 0 ] =  0 ;
         for ( int i =  1 ,q =  0 ;i < length;i++) {
             while (q >  0 && s[i] != s[q])
                 q = next[q- 1 ];
             if (s[i] == s[q])
                 q++;
             next[i] = q;
         }
         return next;
     }
     
     bool kmp(string strA,string strB) {
         int length = strB.size();
         vector< int > next = getNext(strB);
         for ( int i =  0 ,q =  0 ;i <  2 *length;i++) {
             while (q >  0 && strA[i] != strB[q])
                 q = next[q- 1 ];
             if (strA[i] == strB[q])
                 q++;
             if (q == length)
                 return true ;
         }
         return false ;
     }
     
     bool chkRotation(string A,  int lena, string B,  int lenb) {
         if (lena != lenb)
             return false ;
         A += A;
         return kmp(A,B);
     }
};


4、 字符串分块逆序※※※
即,将"dog loves pig" 逆序成 "pig loves dog",以空格为分界,将字符串中的单词顺序逆序。
解法:先将整个字符串逆序,再遍历一遍字符串,找到空格就把当前的单词进行逆序。
class  Reverse {
public:
     void  reverse(string &s, int  start, int  end) {
         char  temp;
        for ( int  i = start,j = end;i < j;i++) {
            temp = s[i];
            s[i] = s[j];
            s[j] = temp;
            j--;
        }
         return ;
    }
    string reverseSentence(string A,  int  n) {
        reverse(A, 0 ,n-1);
         for  ( int  i =  0 ,j =  1 ;j <= n;j++) {
             if  (A[j] == ' ' || j == n) {
                reverse(A,i,j- 1 );
                i = j +  1 ;
            }
        }
         return  A;
    }
};


5、字符串移位
将字符串的前缀移动到字符串的后面,如将 "ABCDE"移位变成 "DEABC"。
解法:先将整个字符串逆序,再将原来的前缀部分(已移动到后部)逆序,最后将原来的后缀(已移动到前部)逆序。

class  Translation {
public:
     void  reverse(string &s, int  start, int  end) {
         char  temp;
        for ( int  i = start,j = end;i < j;i++) {
            temp = s[i];
            s[i] = s[j];
            s[j] = temp;
            j--;
        }
         return ;
    }
    string stringTranslation(string A,  int  n,  int  len) {
        reverse(A, 0 ,n- 1 );
        reverse(A, 0 ,n-len- 1 );
        reverse(A,n-len,n- 1 );
         return  A;
    }
};


6、给定字符串数组,请找到一种拼接顺序,使所有小字符串拼接成的大字符串是所有可能的拼接中字典序最小的。
如  ["abc", "de"]应该拼接为 "abcde"。

解法:
①实际上就是对字符串进行排序,与数值排序不同之处在于大小的比较,两个字符串按字典序来比较大小应该是将两个字符串的各个字符分别比较,哪一个首先遇到较大的字符谁就较大,反之则较小。
②由于排完序后还要使拼接后的大字符串字典序最小,所以 不能按单个字符串的字典序大小来排序,而应该将两个字符串按两种不同的顺序拼接得到的两个字符串的字典序来排序。例如 “ba”和“b”,应该要比较“bab”和“bba”的字典序,前者小所以“ba”较小。
③采用 堆排序算法进行排序。

class Prior {
public :
     int  compare(string A,string B) {
/*
         string str1 = A + B,str2 = B + A;      
        intlength = str1.size();
        for(inti = 0;i < length;i++) {
            if(str1[i] < str2[i])
                return-1;
            if(str1[i] > str2[i])
                return1;
        }
        return0;
*/
        return (A+B > B+A)?1:0;
     }
     
     void  heapify(vector<string> &strArray, int  p, int  length) {
         string temp = strArray[p];
         int  j =  2 *p +  1 ;
         while  (j < length) {
             if  (j+ 1 < length && compare(strArray[j+ 1 ],strArray[j]) ==  1 )
                 j++;
             if  (compare(strArray[j],temp) == 0 )
                 break ;
             strArray[p] = strArray[j];
             p = j;
             j =  2 *p +  1 ;
         }
         strArray[p] = temp;
         return ;
     }
     
     string findSmallest(vector<string> strs,  int  n) {
         string temp;
         for  ( int  p = n/ 2 -  1 ;p >=  0 ;p--) {
             heapify(strs,p,n);
         }
         
         for  ( int  j = n- 1 ;j >  0 ;j--) {
             temp = strs[ 0 ];
             strs[ 0 ] = strs[j];
             strs[j] = temp;
             heapify(strs, 0 ,j);
         }
         
         temp = strs[ 0 ];
         for  ( int  k =  1 ;k < n;k++) {
             temp += strs[k];
         }
         return  temp;
     }
};


7、判断一个字符串是否是合法的括号串
合法的括号串首先不含有别的符号,其次左括号和右括号应该要配对出现,任何一种多都不合法,最后还要保证先左括号后右括号。
解法一:O(n)、O(1)。用一个数num做统计,遍历字符串,遇到‘(’则num加1,遇到‘)’则num减1,遍历期间若num<0则直接返回false,否则继续遍历,遍历结束若num!=0则返回false,否则返回true。
class Parenthesis {
public :
     bool chkParenthesis(string A,  int n) {
         int num =  0 ;
         for ( int i =  0 ;i < n;i++) {
             if (num <  0 )
                 return false ;
             switch (A[i]) {
                 case '(' :
                     num++;
                     break ;
                 case ')' :
                     num--;
                     break ;
                 default :
                     return false ;
             }
         }
         return (num ==  0 )? true : false ;
     }
};
解法二:用栈来实现,遍历字符串,遇到'('则将其入栈,遇到')'时先判断栈是否空,若空则返回false,否则出栈,遍历结束栈空返回true,不空返回false。
class  Parenthesis {
public :
     stack < char > s;
     bool  chkParenthesis(string A,  int  n) {       
         for  ( int  i =  0 ;i < n;i++) {
             switch  (A[i]) {
                 case  '(': 
                    s.push(A[i]);
                     break ;                 
                 case  ')': 
                     if  (s.empty()) 
                         return false ;
                    s.pop();
                     break ;
                 default
                     return false ;
            }
        }
         return  (s.empty())? true:false ;
    }
};

8、求一个字符串中无重复字符的子串的最大长度
解法:遍历字符串,以当前字符结束的无重复字符子串向左延伸最远能到达的位置,要么是当前字符上一次出现的位置,要么是以上一个字符结束的无重复字符子串能到达的位置。因此可以迭代地计算。但是需要一个hash表来存储当前字符上一次出现的位置,需要一个变量来存储以上一个字符结束的无重复字符子串向左能到达的位置。

class  DistinctSubstring {
public :
     int  longestSubstring(string A,  int  n) {
        int index[ 26 ];
        memset(index,- 1 ,sizeof(int)* 26 );         
         int  lastIndex =  0 ;
         int  length =  0 ,maxLength =  0 ;
         for  ( int  i =  0 ;i < n;i++) {
             int  j = ( int )(A[i]-'a');
             if  (index[j] == - 1  || index[j] < lastIndex) {
                length = i - lastIndex +  1 ;
            }
             else  {
                length = i - index[j];
                lastIndex = index[j] +  1 ;
            }

            index[j] = i;
             if  (length > maxLength) 
                maxLength = length;                    
        }
         return  maxLength;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值