重要概念
1.回文
2.子串(连续)
3.子序列(不连续)
4.前缀:指除了最后一个字符以外,一个字符串的全部头部组合。
5.后缀:指除了第一个字符以外,一个字符串的全部尾部组合。
- 例:“ABCDAB”的前缀为[A, AB, ABC, ABCD, ABCDA],后缀为[BCDAB, CDAB, DAB, AB, B],共有元素为”AB”,长度为2;
6.前缀树(Trie树)
7.后缀树与后缀树组
8.匹配
字典序
- 例:“ABCDAB”的前缀为[A, AB, ABC, ABCD, ABCDA],后缀为[BCDAB, CDAB, DAB, AB, B],共有元素为”AB”,长度为2;
重要操作
与数组有关的操作:增删改查
字符的替换
字符串的旋转
题目常见类型
1.规则判断
判断字符串是否符合整数规则,返回整数
判断字符串是否符合浮点数规则,返回浮点数
判断字符串是否符合回文规则
2.数字运算
int与long范围有限,通常用字符串拼大整数,与大整数相关的加减乘除操作,需要模拟笔算的过程,
3.与数组操作有关的类型
数组有关的调整、排序等操作
快速排序的划分过程需要掌握与改写
4.字符计数
哈希表进行统计
固定长度的数组代替哈希表C/C++(ASCII码范围0-255,用256长度数组)
滑动窗口问题,寻找无重复字符子串问题,计算变位词问题
5.动态规划问题
最长公共子串问题
最长公共子序列问题
最长回文子串
最长回文子序列
6.搜索类型(如何将string1变换为string2,每一步变换过程)
宽度优先搜索
深度优先搜索
7.高级算法与数据结构解决的问题
Manacher算法解决最长回文子串问题
KMP算法解决字符串匹配问题
前缀树结构
后缀树与后缀树组
示例一
给定彼此独立的两颗树,判断t1中是否有与t2拓扑结构完全相同的子树。
分析:将两颗树序列化,然后用KMP算法进行字符串匹配。时间复杂度为 O ( N + M ) O(N+M) O(N+M)。
示例二
给定两个字符串str1与str2,如果两个字符串中出现的字符种类一样且每种字符出现的次数也一样,那么str1与str2互为变形词,请判断两个字符是否互为变形词。
分析:使用哈希表分别记录每个字符出现的次数。也可以建立一个256的数组,代替哈希表。
bool simpleWord(char *str1, char *str2, const int &length) {
int num1[256] = { 0 };
int num2[256] = { 0 };
for (int i = 0; i < length; ++i) {
++num1[str1[i]];
}
for (int i = 0; i < length; ++i) {
++num2[str2[i]];
}
bool flag = true;
for (int i = 0; i < length; ++i) {
if (num1[i] != num2[i]) {
flag = false;
break;
}
}
return flag;
}
示例三
如果一个字符串str,把字符串str前面任意的部分挪到后面去形成的字符串叫做str的旋转词。比如str=“1234”,则旋转词有“1234”,“2341”,“3412”,“4123”。判断两个字是否互为旋转词。
分析:最优解时间复杂度为 O ( N ) O(N) O(N)。
1.判断str1与str2是否长度相等
2.如果长度相等,生成str1+str1的大字符串
3.用KMP算法判断大字符串中是否含有str2
#include<iostream>
#include<thread>
#include<string>
#include<vector>
using std::cout;
using std::endl;
void getNext(int *next,const std::string &str) {
int i = 0;
next[0] = -1;
int k = -1;
while (i < (str.length() )) {
if (k == -1 || str[i] == str[k]) {
++i;
++k;
next[i] = k;
}
else {
k = next[k];
}
}
}
bool subString(const std::string &str1, const std::string &str2, std::vector<int> &position) { //查找str2是否在str1中,并将位置存储在position中,可重复可重叠出现
int *next = new int[str2.length()+1];
getNext(next, str2);
int i = 0; // str1
int j = 0;// str2
while (i<str1.length()){
if (j == -1 || str1[i] == str2[j]) {
++i;
++j;
if (j >= str2.length()) {
position.push_back(i - str2.length());
j = next[j];
}
}
else{
j = next[j];
}
}
delete[] next;
if (position.empty()) {
return false;
}
else{
return true;
}
}
bool rotateWord(const std::string str1, const std::string str2) { //判断str2是否为str1的旋转词
std::string doublestr1 = str1 + str1;
std::vector<int> positon;
return subString(doublestr1, str2, positon);
}
int main() {
std::string str1 = "1234";
std::string str2 = "2341";
std::vector<int> position;
cout << rotateWord(str1, str2) << endl;
while (true)
{
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
}
return 0;
}
示例四
给定一个字符串str,请在单词间做逆序调整。
举例:
“pig loves dog”逆序成“dog loves pig”
分析:
1.实现将字符串局部所有字符逆序的函数f
2.利用f将字符串所有字符逆序
“god sevol gip”
3.找到逆序后的字符串中每一个单词的区域利用f将每一个单词的区域逆序
#include<iostream>
#include<thread>
#include<string>
#include<vector>
using std::cout;
using std::endl;
void reverseWord(std::string &str) { //字符串反向
for (int i = 0; i < str.length() / 2; ++i) {
auto temp = str[i];
str[i] = str[str.length() - i - 1];
str[str.length() - i - 1] = temp;
}
}
void reverseWord(std::string &str, const int &start,const int &end) { //字符串从start到end 反转
for (int i = 0; i < (end - start + 1) / 2; ++i) {
char temp = str[start + i];
str[start + i] = str[end - i];
str[end - i] = temp;
}
}
void reverseSentence(std::string &str) {
reverseWord(str);
int start = 0;
int end = -2;
for (int i = 0; i < str.length(); ++i) {
if (str[i] == ' ') {
start = end + 2;
end = i - 1;
reverseWord(str, start, end);
}
if (i == (str.length() - 1)) {
start = end + 2;
end = str.length() - 1;
reverseWord(str, start, end);
}
}
}
int main() {
std::string str1 = "dog loves pig.";
std::string str2 = "0123456789";
std::vector<int> position;
reverseSentence(str1);
for (auto i : str1) {
cout << i << endl;
}
while (true)
{
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
}
return 0;
}
示例五
给定一个字符串str,和一个整数i。i代表str中的位置,将str[0…i]移到右侧,str[i+1…N-1]移到左侧。要求:时间复杂度为 O ( N ) O(N) O(N),额外空间复杂度为 O ( 1 ) O(1) O(1)。
举例:str=“ABCDE”,i=2,将str调整为“CDEAB”
分析:
1.首先将0-i逆序
2.将i+1-N-1逆序
3.将str整体逆序
#include<iostream>
#include<thread>
#include<string>
#include<vector>
using std::cout;
using std::endl;
void reverseWord(std::string &str) { //字符串反向
for (int i = 0; i < str.length() / 2; ++i) {
auto temp = str[i];
str[i] = str[str.length() - i - 1];
str[str.length() - i - 1] = temp;
}
}
void reverseWord(std::string &str, const int &start,const int &end) { //字符串从start到end 反转
for (int i = 0; i < (end - start + 1) / 2; ++i) {
char temp = str[start + i];
str[start + i] = str[end - i];
str[end - i] = temp;
}
}
void translationWord(std::string &str, const int &position) { // 将字符串0~position移到最右侧
reverseWord(str, 0, position);
reverseWord(str, position + 1, str.length() - 1);
reverseWord(str);
}
int main() {
std::string str1 = "dog loves pig.";
std::string str2 = "0123456789";
std::vector<int> position;
translationWord(str2, 5);
for (auto i : str2) {
cout << i << endl;
}
while (true)
{
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
}
return 0;
}
示例六
给定一个字符串类型的数组strs,请找到一种拼接顺序,使得将所有字符串拼接起来的大字符串是所有可能性中字典顺序最小的,并返回这个大字符串。
举例:str=[“abc”,“de”]。可以拼接成“abcde”“deabc”,选择字典顺序小的“abcde”。
分析:直接对字符串排序会出现问题,例如[“b”,“ba”]会拼接成“bba”,但是“bab”是最小。应当比较str1+str2与str2+str1的大小。
#include<iostream>
#include<thread>
#include<string>
#include<vector>
#include<algorithm>
using std::cout;
using std::endl;
bool cmp(std::string str1, std::string str2) {
return str1 + str2 < str2 + str1 ? true : false;
}
std::string minJoint(std::vector<std::string> strs) {
sort(strs.begin(), strs.end(),cmp);
std::string str = "";
for (auto i : strs) {
str += i;
}
return str;
}
int main() {
std::string str1 = "dog loves pig.";
std::string str2 = "0123456789";
std::string strs[2] = { "b","a" };
std::vector<std::string> position = {"b","ba"};
str1 = minJoint(position);
for (auto i : str1) {
cout << i << endl;
}
while (true)
{
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
}
return 0;
}
示例七
给定一个字符串str,将其中所有空格字符替换成“%20”,假设str后面有足够的空间。
分析:
1.计算出空格数量,得到最终字符的长度。
2.从后往前写入字符。
#include<iostream>
#include<thread>
#include<string>
#include<vector>
#include<algorithm>
using std::cout;
using std::endl;
void replaceSpace(std::string &str) {//将空格替换为“%20”
int spaceNum = 0;
int lenOld = str.length() - 1;
for (auto i : str) {
if (i == ' ') {
++spaceNum;
}
}
std::string addition(spaceNum * 2, ' ');
str = str + addition;
int lenNew = lenOld + 2 * spaceNum;
for (int i = lenOld; i >= 0; --i) {
if (str[i] != ' ') {
str[lenNew--] = str[i];
}
else if (str[i] == ' ') {
str[lenNew--] = '0';
str[lenNew--] = '2';
str[lenNew--] = '%';
}
}
}
int main() {
std::string str1 = "dog loves pig";
std::string str2 = "0123456789";
std::string strs[2] = { "b","a" };
std::vector<std::string> position = {"b","ba"};
int n = 10;
std::string str(n,'c');
replaceSpace(str1);
for (auto i : str1) {
cout << i << endl;
}
while (true)
{
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
}
return 0;
}
示例八
给定一个字符串,其中只包含左括号或右括号,判断是不是整体有效的括号字符串。
时间复杂度为 O ( N ) O(N) O(N),额外空间复杂度为 O ( 1 ) O(1) O(1)。
分析:使用num计算左右括号数,如果是左括号num++,如果是右括号num–,遍历的过程中如果出现num<0,则直接返回false,最终如果num==0,则返回true,否则为false。
实例九
给定一个字符串str,返回str的最长无重复字符子串的长度。
分析:求出以str中每个字符结尾的情况下,最长无重复字符子串的长度,并在其中找出最大值返回。
1.使用哈希表map记录每一种字符之前出现的位置。
2.整型变量pre,代表上一个字符结尾情况下,最长无重复子串的长度。
3.每次比较第i个字符上次出现的位置与第i-1个字符无重复子串的头的位置,若第i个字符上次出现的位置在第i-1个字符无重复子串的头的左侧,则len[i] = pre+1;若在右侧len[i] =i- map[i]+1;
时间复杂度为 O ( N ) O(N) O(N),额外空间复杂度为 O ( N ) O(N) O(N)。
#include<iostream>
#include<thread>
#include<string>
#include<vector>
#include<algorithm>
using std::cout;
using std::endl;
int maxNoRepeatLength(const std::string &str) {
int hash[256] = { -1 };
int preLength = 1;
memset(hash, -1, 256 * sizeof(int));
hash[str[0]] = 0;
int maxLen = 0;
for (int i = 1; i < str.length(); ++i){
if (hash[str[i]] == -1) {
int len = preLength + 1;
preLength = len;
maxLen = (len > maxLen ? len : maxLen);
hash[str[i]] = i;
}
else
{
if ((i - hash[str[i]]) > preLength) {
int len = preLength + 1;
preLength = len;
maxLen = (len > maxLen ? len : maxLen);
hash[str[i]] = i;
}
else
{
int len = i - hash[str[i]] + 1;
preLength = len;
maxLen = (len > maxLen ? len : maxLen);
hash[str[i]] = i;
}
}
}
return maxLen;
}
int main() {
std::string str1 = "abcdecc";
std::string str2 = "0123456789";
std::string strs[2] = { "b","a" };
std::vector<std::string> position = {"b","ba"};
int n = 10;
std::string str(n,'c');
int i = maxNoRepeatLength(str1);
cout << i << endl;
while (true)
{
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
}
return 0;
}