三步翻转法:区间翻转操作实现循环移位


题目:给定一个长度为 n n n 的字符串 s s s,将 s s s 进行循环右移 k k k 位。

显然,将字符串循环右移 n n n 位后,得到原字符串。循环移动 k k k 位与循环移动 k m o d    n k \mod n kmodn 位后的字符串相同。

s s s 进行循环右移 k k k 位,相当于:截取 s s s 的后 k k k 个字符,将其连接到剩下的子串的开头。

void rotateRight(string &s, int k){
	int n = s.size();
	k %= n;
	if(k==0) return;
	string sub1 = s.substr(0, n-k);	// 截取 s 的前 n-k 个字符
	string sub2 = s.substr(n-k);	// 截取 s 的后 k 个字符
	s = sub2 + sub1;
}

或者按照数组的方式,访问并确定 s s s 的每个字符。

void rotateRight(string &s, int k){
	int n = s.size();
	k %= n;
	if(k==0) return;
	string tmp = s;
	for(int i=0; i<k; ++i){	// 确定前 k 个字符是原字符串的后 k 个字符
		s[i] = tmp[i+n-k];
	}
	for(int i=k; i<n; ++i){	// 确定后 n-k 个字符是原字符串的前 n-k 个字符
		s[i] = tmp[i-k];
	}
}

上面程序的时间复杂度是 O ( n ) O(n) O(n),空间复杂度是 O ( n ) O(n) O(n)

三步翻转法

0 < k < n 00<k<n,三步翻转法:

  1. 将区间 [ 0 , n − 1 − k ] [0, n-1-k] [0,n1k] 中的字符进行对称翻转,即对字符串前 n − k n-k nk 个字符进行翻转
  2. 将区间 [ n − k , n − 1 ] [n-k,n-1] [nk,n1] 中的字符进行对称翻转,即对字符串后 k k k 项进行翻转
  3. 将区间 [ 0 , n − 1 ] [0,n-1] [0,n1] 中的字符进行翻转,即对整个字符串进行翻转

三步翻转法可以实现数组循环右移操作,其时间复杂度为 O ( n ) O(n) O(n),空间复杂度为 O ( 1 ) O(1) O(1)

void rotateRight(string &s, int k){
	int n = s.size();
	k %= n;
	if(k==n) return;
	reverse(s.begin(), s.begin()+n-k);
	reverse(s.begin()+n-k, s.end());
	reverse(s.begin(), s.end());
}

存在等效方法:

  1. 将区间 [ 0 , n − 1 ] [0,n-1] [0,n1] 中的字符进行翻转,即对整个字符串进行翻转
  2. 将区间 [ 0 , k − 1 ] [0, k-1] [0,k1] 中的字符进行对称翻转,即对字符串前 k k k 个字符进行翻转
  3. 将区间 [ k , n − 1 ] [k,n-1] [k,n1] 中的字符进行对称翻转,即对字符串后 n − k n-k nk 项进行翻转

可以这么理解:

nums = "----->-->"; k =3
result = "-->----->";

reverse "----->-->" we can get "<--<-----"
reverse "<--" we can get "--><-----"
reverse "<-----" we can get "-->----->"
this visualization help me figure it out :)

上面的模拟来源于 leetcode 题189. 旋转数组官方题解下的评论。

下面是 C++ 代码实现:

void rotateRight(string &s, int k){
	int n = s.size();
	k %= n;
	if(k==n) return;
	reverse(s.begin(), s.end());
	reverse(s.begin(), s.begin()+k);
	reverse(s.begin()+k, s.end());
}

reverse(first, last) 函数模板在定义在头文件 中,功能是反转 [first, last) 范围内元素的顺序。它的时间复杂度是 O ( n ) O(n) O(n),空间复杂度是 O ( 1 ) O(1) O(1) n n n 是 first 到 last 之间的距离。

也可以自己实现 reverse() 函数的功能。

#include
using namespace std;

// 反转字符串 s 的范围 [left, right] 内字符的顺序
void reverseString(string &s, int left, int right){
	while(left<right){
		char tmp = s[left];
		s[left] = s[right];
		s[right] = tmp;
		++left;
		--right;
	}
}

// 将字符串 s 进行循环右移 k 位
void rotateRight(string &s, int k){
	int n = s.size();
	k %= n;
	if(k==n) return;
	reverseString(s, 0, n-1);
	reverseString(s, 0, k-1);
	reverseString(s, k, n-1);
}

int main(){
	string s = "12345678";
	rotateRight(s, 3);
	cout << s << endl;	// 输出 67812345
	return 0;
}

如果题目要求将长度为 n n n 的字符串 s s s 循环左移 k k k 位,即求:将 s s s 循环右移 n − k n-k nk 位。


刷题题解目录

你可能感兴趣的:(数据结构与算法,字符串,数据结构,算法)