字符串
定义
字符串是若干字符组成的有限序列;C语言中,把一个字符串存入一个数组时,也把结束符 '\0'存入数组,并以此作为该字符串是否结束的标志。
char a[5] = "asd";
for (int i = 0; a[i] != '\0'; i++) {
}
C++中,提供一个string类,string类会提供 size接口,可以用来判断string类字符串是否结束,就不用'\0'来判断是否结束。
string a = "asd";
for (int i = 0; i < a.size(); i++) {
}
双指针法
在数组、链表、字符串中很常用
力扣344反转字符串
编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 s 的形式给出。
不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。
class Solution {
public:
void reverseString(vector<char>& s) {
//双指针法解题
int len=s.size();
int i=0;
int j=len-1;
for(i=0;i<len/2;i++,j--){
swap(s[i],s[j]);
}
}
};
力扣剑指Offer05替换空格
请实现一个函数,把字符串 s 中的每个空格替换成"%20"。
扩充数组到每个空格替换成"%20"之后的大小。
从后向前替换空格,也就是双指针法,i指向新长度的末尾,j指向旧长度的末尾。
class Solution {
public:
string replaceSpace(string s) {
//1.统计字符串中有多少个空格
int count=0;
int oldsize=s.size();
for(int i=0;i<s.size();i++){
if(s[i]==' '){
count++;
}
}
//2.给字符串扩容
s.resize(s.size() + count * 2);
int newsize=s.size();
//3.替换空格
int j=newsize-1;
for(int z=oldsize-1;z>=0;z--){
if(s[z]!=' '){
s[j]=s[z];
j--;
}
else{
s[j]='0';
s[j-1]='2';
s[j-2]='%';
j=j-3;
}
}
return s;
}
};
力扣27移除元素
给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。
不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
数组的元素在内存地址中是连续的,不能单独删除数组中的某个元素,只能覆盖。
通过一个快指针和慢指针在一个for循环下完成两个for循环的工作
快指针:寻找新数组的元素,心数组就是不含有目标元素的数组;慢指针:更新新数组下标的位置
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
//暴力法求解
/*int n=nums.size();
int count=0;
for(int i=0;i<n;i++){
if(nums[i]==val){
for(int j=i+1;j<n;j++){
nums[j-1]=nums[j];
}
i--; //因为下标为i的数字都向前移动了一位 所以下标也向前移动一位
n--;//数组的大小也-1
}
}
return n;*/
//双指针法
int slow=0;//慢指针,指向新的元素
int fast=0;//快指针,指向原来的数组
for(fast=0;fast<nums.size();fast++){
if(nums[fast]!=val){ //值不为要找到的元素
nums[slow]=nums[fast];
slow++;
}
}
return slow;
}
};
力扣151反转字符串里的单词
给你一个字符串 s ,请你反转字符串中 单词 的顺序
输入:s = "the sky is blue"
输出:"blue is sky the"
移除多余空格
将整个字符串反转
将每个单词反转
class Solution {
public:
//1.翻转字符串
void reverse(string& s,int start,int end){
for(int i=start,j=end;i<j;i++,j--){
swap(s[i],s[j]);//交换
}
}
//2.移除空格:双指针法
void eraseSpace(string& s){
int slow=0;
for(int fast=0;fast<s.size();fast++){
if(s[fast]!=' '){ //如果不是空格就执行操作,相当于删除了空格
if(slow!=0){//如果slow不是在开始位置,就要先打一个空格,因为每个单词的前面要有一个空格
s[slow]=' ';
slow++;
}
while(fast<s.size()&&s[fast]!=' '){//遇到空格就说名单词结束
s[slow]=s[fast];
slow++;//向后移动
fast++;
}
}
}
s.resize(slow);
}
//3.反转字符中的单词
string reverseWords(string s) {
//先移除空格
eraseSpace(s);
//反转整个字符串
reverse(s,0,s.size()-1);
//反转每一个单词
int start=0;
for(int i=0;i<=s.size();i++){
if(s[i]==' '||i==s.size()){//到达末尾或遇到空格都表明要反转这个单词
reverse(s,start,i-1);//左闭右闭区间
start=i+1;//更新下一个单词的开始下标start
}
}
return s;
}
};
反转系列
力扣541反转字符串II
给定一个字符串 s 和一个整数 k,从字符串开头算起,每计数至 2k 个字符,就反转这 2k 字符中的前 k 个字符。
如果剩余字符少于 k 个,则将剩余字符全部反转。
如果剩余字符小于 2k 但大于或等于 k 个,则反转前 k 个字符,其余字符保持原样。
模拟:实现题目中规定的反转规则
在遍历字符串的过程中,让i+=(2*k),i每次移动2*k即可,然后判断是否需要有反转的区间。
class Solution {
public:
void reverse(string &s,int start,int end){
//直接进行反转字符串操作
for(int i=start,j=end;i<j;i++,j--){
//交换首尾的字符
swap(s[i],s[j]);
}
}
string reverseStr(string s, int k) {
//i 2k个往后蹦
for(int i=0;i<s.size();i+=2*k){
//如果剩余字符小于 2k 但大于或等于 k 个,则反转前 k 个字符,其余字符保持原样
if(i+k<=s.size()){
reverse(s,i,i+k-1);//交换满足条件位置的所有
continue;
}
//如果剩余字符少于 k 个,则将剩余字符全部反转。
reverse(s,i,s.size()-1);
}
return s;
}
};
力扣剑指Offer58左旋转字符串
字符串的左旋转操作是把字符串前面的若干个字符转移到字符串的尾部。请定义一个函数实现字符串左旋转操作的功能。比如,输入字符串"abcdefg"和数字2,该函数将返回左旋转两位得到的结果"cdefgab"。
反转区间为前n的子串
反转区间为n到末尾的子串
反转整个字符串
class Solution {
public:
void reverse(string& s,int start,int end){
for(int i=start,j=end;i<j;i++,j--){
swap(s[i],s[j]);
}
}
string reverseLeftWords(string s, int n) {
//1. 反转区间为前n的子串
reverse(s,0,n-1);
//2. 反转区间为n到末尾的子串
reverse(s,n,s.size()-1);
//3. 反转整个字符串
reverse(s,0,s.size()-1);
return s;
}
};
KMP系列
算法还没有很好的掌握,感觉只是明白了其中部分原理。
力扣28实现 strStr()
实现 strStr() 函数。
给定一个 haystack 字符串和一个 needle 字符串,在 haystack 字符串中找出 needle 字符串出现的第一个位置 (从0开始)。如果不存在,则返回 -1。
class Solution {
public:
//获得next数组
void getNext(int *next,string &s){
//1.初始化
//定义两个指针i和j,j指向前缀末尾位置,i指向后缀末尾位置
int j=0;//前缀的末尾是0
next[0]=0;//next数组的第一位是0 没有相等的前后缀
for(int i=1;i<s.size();i++){ //后缀的末尾是1
//2.处理不相等情况
while(j>0&&s[i]!=s[j]){//j要保证大于0 因为有取j-1的操作
j=next[j-1];//j的值追溯到next[j-1]位置
}
//3.处理相等的情况
if(s[i]==s[j]){
j++;
}
//更新next数组
next[i]=j;
}
//2.
}
//KMP算法
int strStr(string haystack, string needle) {
if(needle.size()==0){
return 0;
}
int next[needle.size()];
getNext(next,needle);
int j=0;//因为next数组里记录的起始位置为0
for(int i=0;i<haystack.size();i++){//i从0开始
while(j>0&&haystack[i]!=needle[j]){//对应不匹配的情况
j=next[j-1];//j 寻找之前匹配的位置
}
if(haystack[i]==needle[j]){//匹配,j和i同时向后移动
j++;//i的增量在循环中
}
if(j==needle.size()){//文本串s里出现了模式串t
return(i-needle.size()+1);
}
}
return -1;
}
};
力扣459重复的子字符串
给定一个非空的字符串 s ,检查是否可以通过由它的一个子串重复多次构成。
class Solution {
public:
//利用kmp算法求解
void getNext(int *next,const string& s){
next[0]=0;//初始化数组
int j=0;
for(int i=1;i<s.size();i++){
while(j>0&&s[i]!=s[j]){//不相等的情况
j=next[j-1];//往前一位找
}
if(s[i]==s[j]){//相等的情况
j++;
}
next[i]=j;//j的值是数组的值
}
}
bool repeatedSubstringPattern(string s) {
/*
//移动匹配算法
string ss=s+s;
//掐头去尾
ss.erase(ss.begin());
ss.erase(ss.end()-1);
if (ss.find(s) != std::string::npos) return true;
return false;*/
if(s.size()==0){ //如果字符串的长度是0
return false;//不废话 返回
}
int next[s.size()];//数组的长度是字符串的长度
getNext(next,s);
int len=s.size();//长度是字符串的长度
//len % (len - (next[len - 1] + 1)) == 0
//则说明数组的长度正好可以被 (数组长度-最长相等前后缀的长度) 整除,说明该字符串有重复的子字符串。
if(next[len-1]!=0&&len%(len-(next[len-1]))==0){
return true;//包含重复子串
}
return false;//不包含重复子串
}
};