目录
字符串基础
复习一下
-------------浅浅回忆一下:
首先我们来讨论一下char str1[ ]="Hello";和char* str2="Hello";(二者当形参是一样的东西)有哪些区别。
(1)栈区 常量区
(2)str1[0]='h'对(访问栈区空间) str2[0]='h'错(访问常量区空间,只能读不能写)
(3)sizeof(str1)=6(数组大小) sizeof(str2)=4(指针大小)
(4)str1="haha"错(str1指向的地址空间不能改变,数组首元素首地址,相当于常量,不能当左 值) str2="haha"对(指针变量可以被赋值)
注: sizeof(*str1) str1是首元素地址,*str1是首元素H,字符sizeof为1。
*(++str1)不能运算,str1是常量不能对自身++--运算。
---------------正文开始:
替换数字
题目:给定一个字符串 s,它包含小写字母和数字字符,请编写一个函数,将字符串中的字母字符保持不变,而将每个数字字符替换为number。
例如,对于输入字符串 "a1b2c",函数应该将其转换为 "anumberbnumberc"。
我们用双指针来解决,步骤:
(1)扩容,原字符串为s.size(),新字符串为s.size()+字符个数*5,两个指针分别指向新字符串队尾,和原字符串队尾。
(2)如果碰到了字符,那就放入新字符串。
(3)如果碰到数字,新字符串的指针就前移5位把num放进去。
(4)重复(2)或(3)操作,直到两指针到同一位置。
代码实现:
#include<iostream>
#include<string.h>
using namespace std;
int main()
{
string s;
cin >> s;
int a = s.size();
int numc = 0;
for (int i = 0; i < a; i++)
{
if (s[i] >= '0' && s[i] <= '9')
numc++;
}
s.resize(s.size() + numc * 5);
int b = s.size();
for (int i = a - 1, j = b - 1; j != i; i--, j--)
{
if (s[i] >= '0' && s[i] <= '9')
{
s[j] = 'r';
s[j - 1] = 'e';
s[j - 2] = 'b';
s[j - 3] = 'm';
s[j - 4] = 'u';
s[j - 5] = 'n';
j -= 5;
}
else
s[j] = s[i];
}
cout << s << endl;
return 0;
}
翻转单词
题目:oh my god 翻转成 god my oh。
思路:首先先整体翻转,dog ym ho,然后再单个单词翻转god my oh,翻转可以写一个翻转函数来搞定。
#include<iostream>
#include<math.h>
using namespace std;
void Reverse(char str[], int l, int r)
{
while (l < r)
{
swap(str[l], str[r]);
l++;
r--;
}
}
void re(char str[])
{
if (str == NULL)return;
//整体翻转
Reverse(str, 0, strlen(str) - 1);
//单词翻转
int l = 0;
int r = 0;
while (r < strlen(str))
{
//找到第一个非空字符
while (str[l] == ' ')
{
l++; r++;
}
while (str[r] != ' ' && str[r] != '\0')
r++;
Reverse(str,l,r-1);
l = r;
}
cout << str << endl;
}
int main()
{
char str[] = "the sky is blue";
re(str);
return 0;
}
进阶版:151. 反转字符串中的单词 - 力扣(LeetCode)
把翻转的单词放进ss中并且每个单词中间只有一个空格,并且注意特判情况。
class Solution {
public:
void rever(string &s, int l, int r) {
while (l < r) {
swap(s[l], s[r]);
l++;
r--;
}
}
string reverseWords(string s) {
rever(s, 0, s.size() - 1);
int l = 0;
int r = 0;
char ss[10000];
int k=0;
int t=0;
while (r < s.size()) {
while (s[l] == ' ') {
//"aa abc ac "这种情况需要特判
if(l==s.size()-1&&s[l]==' ')return ss;
l++;
r++;
}
while (s[r] != ' '&& r<s.size())
r++;
rever(s, l, r - 1);
if(t==1)
ss[k++]=' ';
//把翻转的每个单词放ss里
for(int i=l;i<=r-1;i++)
{
ss[k]=s[i];
k++;
}
l = r;
t=1;
}
return ss;
}
};
变形:把字符串尾部的若干个字符转移到字符串的前面。给定一个字符串 s 和一个正整数 k,请编写一个函数,将字符串中的后面 k 个字符移到字符串的前面,例如,对于输入字符串 "abcdefg" 和整数 2,函数应该将其转换为 "fgabcde"。
这种情况就是把它先全部翻转gfedcba,然后在k=2那里拆分翻转,fgabcde。
KMP
经典的字符串匹配算法,实现strstr()。(用空间换时间)
KMP的主要思想是当出现字符串不匹配时,可以知道一部分之前已经匹配的文本内容,可以利用这些信息避免从头再去做匹配。
例:主串:abcabcdefabcabcf,匹配串:abcabcf。
如果我们匹配错了就从下一个开始找,就很麻烦,所以我们想在匹配失败中吸取些有用的东西,避免我们从头开始匹配,那么Next数组就记录了主串与匹配串匹配失败时,匹配串应该从哪里开始匹配。
abcabc的前缀是:a ab abc abca abcab后缀是:c bc abc cabc bcabc。它的前缀后缀字符串相同的最大长度是3,abc。
而Next数组就是用来存任意位置对应的前缀后缀字符串相同的最大匹配长度,因为任何位置都可能匹配失败。
计算Next数组:aababbabaaaaba
(1)Next[0]=0;
(2)当前字符和前一个字符的Next数组的数对应的字符相比,假设字符串s,当前字符就和s[0]相比,如果相同则Next得前一个Next数加1,如果不相等则跳到当前位置的前一个位置的Next的值的位置的字符去比对。
接下来继续。
b和a不相等,跳到当前位置的前一个位置的Next的值的位置去比对。
(3)当前位置的next值已经为0,那么这个字符的next为0。
当前字符a跳到下标为0的字符位置比对,相同,前一个next值加1。
中间省略。
相同了,当前Next+1。
最终结果
Next数组的运算步骤:
(1)Next[0]=0;
(2) 匹[i]=匹[N[i-1]] N[i]=N[i-1]+1
(3) != N[i-1]=0,N[i]=0; N[i-1]!=0(往回找,重复第一步)
匹配步骤:
(1)比较主串和匹配串:相等:++(两个人同时往后走)。不相等:匹=0,主++;匹!=0,匹=Next[匹-1];
(2)检测:匹走完返回主-匹位置,主走完则false。
#include<iostream>
using namespace std;
int* GetNext(const char* pipei)
{
int* Next = (int*)malloc(sizeof(int) * strlen(pipei));
Next[0] = 0;
int i = 1;
int j = i - 1;
while (i < strlen(pipei)) {
if (pipei[i] == pipei[Next[j]])
{
Next[i] = Next[j] + 1;
i++;
j = i - 1;
}
else if (Next[j] == 0)
{
Next[i] = 0;
i++;
j = i - 1;
}
else
{
j = Next[j] - 1;
}
}
return Next;
}
int KMP(const char* zhu, const char* pipei)
{
if (zhu == NULL || pipei == NULL)return -1;
int* Next = GetNext(pipei);
int i = 0;
int j = 0;
while (i < strlen(zhu) && j < strlen(pipei))
{
if (zhu[i] == pipei[j])
{
i++;
j++;
}
else
{
//匹配到头了
if (j == 0)i++;
//匹配串跳转
else j = Next[j - 1];
}
}
//检测
if(j==strlen(pipei))return i-j;
else return -1;
}
int main()
{
int res = KMP("abacabacabcaaaaaa", "aa");
cout << res << endl;
return 0;
}
Sunday
sunday算法的next是字符出现的位置,初始化都为-1。
匹配方式:
第一次匹配失败了,在下一个标红位置找标红字符在匹配串中的位置,然后与之对应。
若主串标红字符没有在匹配串出现过则直接把下一个字符当匹配头位置。
由此可见找到了,这就是sunday的匹配步骤(若匹配串是wnn,则对标最后一个n)。
(1)Next:遍历匹配串。
(2)标记主串比较匹配串与主串,相等则++,不相等则从这个位置开始比较:匹配头位置 + 匹配串长度 - Next[主串[匹配头位置 + 匹配串长度]],匹配串头从开始重新匹配。
#include<iostream>
#include<string.h>
using namespace std;
int Sunday(const char* zhu, const char* pipei)
{
if (zhu == NULL || pipei == NULL)return -1;
int* Next = (int*)malloc(sizeof(int) * 256);
//如果匹配到不存在在匹配串的赋值为-1正好一减就是加一,移动到下一个字符了
memset(Next, -1, sizeof(int) * 256);
for (int i = 0; i < strlen(pipei); i++)
Next[pipei[i]] = i;
int i = 0;
int j = 0;
//标记谁与匹配串头匹配
int k = i;
while (i < strlen(zhu) && j < strlen(pipei))
{
if (zhu[i] == pipei[j])
{
i++;
j++;
}
else
{
if (k + strlen(pipei) < strlen(zhu))
{
//主串跳转到下次与匹配串开始比较的地方
i = k + strlen(pipei) - Next[zhu[k + strlen(pipei)]];
j = 0;
k = i;
}
else return -1;
}
}
if (j == strlen(pipei))return k;
else return -1;
}
int main()
{
cout << Sunday("aaaaabcd", "b");
return 0;
}