字符串
1、反转字符串
要求:反转一个字符串,但是不能创建新的字符串。
**思路:**定义一个头尾指针,不断交换。
伪代码:
len = str.size();
for(i = 0, j = len - 1; i < j; i++,j--)
{
swap(str(i),str(j));
}
2、反转字符串Ⅱ
给一个字符串,把每 2 k 2k 2k段里的前 k k k个字符串进行反转,如果最后一段不够 2 k 2k 2k,那么就反转 k k k个,如果 k k k也不够,那么就全反转。
如: k = 3 k=3 k=3, a b c g e f g z abcgefgz abcgefgz,输出为 c b a g e f z g cbagefzg cbagefzg
伪代码:
for(i = 0; i < str.size; i = i+2k)
{
if(i+k <= str.size)
{
reverse(str, i , i+k)
continue;
}
reverse(str,i,str.size);
}
3、反转字符串里的单词
要求: 空格空格hello world 空格空格翻转为 world hello 并删除空格。
**思路:**先直接删除多余的空格,把整个字符串全部翻转,然后在把每一个单词里面的字符全翻转。
**伪代码:**使用双指针,类似于移除数组元素的思路移除空格。然后在翻转全部字符串,在根据单词之间空格的位置翻转单词。
#include<iostream>
#include<cstring>
using namespace std;
void reverse(string& s, int start, int end)
{
for (int i = start, j = end; i < j; i++, j--)
{
swap(s[i], s[j]);
}
}
int main(void)
{
string str = " hello world l ";
int slow_index = 0;
int fast_index = 0;
while(str[fast_index] == ' ' && fast_index < str.size()) fast_index++;
for(;fast_index < str.size();fast_index++)
{
if(str[fast_index] == str[fast_index-1] && str[fast_index] == ' ' && fast_index > 1)
continue;
else
{
str[slow_index++] = str[fast_index];
}
//slow_index++;
}
str.resize(slow_index);//设置字符串占据的空间大小
reverse(str,0,str.size()-1);
cout << str << endl;
int temp = 0;
for(int i = 0;i < str.size();++i)
{
if(str[i] == ' ')
{
reverse(str,temp,i-1);
temp = i + 1;
}
}
reverse(str,temp,str.size()-1);
cout << str << endl;
return 0;
}
4、KMP算法理论基础
KMP算法解决的就是字符串匹配的问题:给出一个字符串aabaabaaf,在给出一个模式串:aabaaf。
前缀与后缀:
前缀是包含首字母,不包含尾字母的所有子串。如aabaaf的前缀有a,aa,aab,aaba,aabaa,后缀是包含尾字母但是不包含首字母的所有字符串,aabaaf的后缀有f,af,aaf,baaf,abaaf。
最长相等前后缀:
a的最长相等前后缀就0,aa的最长相等前后缀为1,aab的最长相等前后缀为0,aaba的最长相等前后缀为1,aabaa的最长相等前后缀为2,aabaaf的最长相等前后缀为0,那么他的前缀表就是010120。
使用前缀表进行匹配:
对aabaabaaf用模式串aabaaf进行匹配时,匹配到字母f时失效,已经匹配过的字符串为aabaa,他的最长相等前后缀为2,所有要跳到2来开始进行匹配。因为相等前后缀的性质。
next数组:
next数组中放的是前缀表。
5、KMP算法的实现
next数组初始化:
初始化→前后缀不相同→前后缀相同→更新
//定义i指向后缀末尾位置,j指向前缀末尾位置
j = 0; next[0] = 0;
for(i=1;i<s.size();i++)
{
while(s[i] != s[j] && j > 0)//这里不能写成if,因为回退是一个连续回退的过程
{
j = next[j-1];
}
if(s[i] == s[j])
{
j++;
next[i] = j;
}
}
6、重复子串
要求:给一个字符串,看这个字符串是不是由重复的字符串组成的
**思路(移除匹配):**如果一个字符串abcabc,确定其是由重复字符串组成的。即:
str = "abcabc";
temp1 = str.size()/2;
str = "abcabc" + "abcabc";//如果把由重复字符串组成的字符串,那么这样拼接起来,str1的后半部分和str2的
//前半部分组成的字符串必然和原字符串一样
str = str[1:str.size()-2];//去掉第一个字母和第2个字母
return str.find();
**思路(KMP算法):**最小重复子串就是最长相等前后缀不包含的子串
如果len % (len - (next[len - 1] + 1)) == 0 ,则说明数组的长度正好可以被 (数组长度-最长相等前后缀的长度) 整除 ,说明该字符串有重复的子字符串。
数组长度减去最长相同前后缀的长度相当于是第一个周期的长度,也就是一个周期的长度,如果这个周期可以被整除,就说明整个数组就是这个周期的循环。
void getNext (int* next, const string& s){
next[0] = -1;
int j = -1;
for(int i = 1;i < s.size(); i++){
while(j >= 0 && s[i] != s[j + 1]) {
j = next[j];
}
if(s[i] == s[j + 1]) {
j++;
}
next[i] = j;
}
}
bool repeatedSubstringPattern (string s) {
if (s.size() == 0) {
return false;
}
int next[s.size()];
getNext(next, s);
int len = s.size();
if (next[len - 1] != -1 && len % (len - (next[len - 1] + 1)) == 0) {
return true;
}
return false;
}