题目来自于
https://github.com/julycoding/The-Art-Of-Programming-By-July/blob/master/ebook/zh/01.00.md
非常感谢July大神和众网友。
本博文初衷是为了方便自己准备面试,有一篇文章可以看所有的东西,省的翻来翻去。
代码是手打的Qt C++,解法中不考虑实现最简单的毫无难度的暴力解法了。
因为很短,不区分*.cc和*.hh文件了,都写在一起; 注释初衷也是为了自己理解。
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1.1 旋转字符串
题目描述
给定一个字符串,要求把字符串前面的若干个字符移动到字符串的尾部,如把字符串“abcdef”前面的2个字符'a'和'b'移动到字符串的尾部,使得原字符串变成字符串“cdefab”。请写一个函数完成此功能,要求对长度为n的字符串操作的时间复杂度为 O(n),空间复杂度为 O(1)。
分析与C++实现
// 编程之美:1.1旋转字符串
// 题目:给定一个字符串,要求把字符串前面的若干个字符移动到字符串的尾部,
// 如把字符串“abcdef”前面的2个字符'a'和'b'移动到字符串的尾部,使得原
// 字符串变成字符串“cdefab”。请写一个函数完成此功能,要求对长度为n的字
// 符串操作的时间复杂度为 O(n),空间复杂度为 O(1)。
#include <iostream>
#include <string>
using namespace std;
// 记得用reference,不然改变后的string传不回来
void leftShiftString(string& str, int shiftNum);
void reverseString(string& str, int from, int to);
void rotate(string& str, int shiftNum);
int main()
{
string test_str = "abcd";
int shiftNumber = 2;
cout << "before: " << test_str << endl;
//leftShiftString(test_str, shiftNumber);
rotate(test_str, shiftNumber);
cout << "after : " << test_str << endl;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// 推荐解法: 三步翻转法
// 将一个字符串分成X和Y两个部分,在每部分字符串上定义反转操作,如X^T,即把X的所有字符反转
// (如,X="abc",那么X^T="cba"),那么就得到下面的结论:(X^TY^T)^T=YX,显然就解决了字符串的反转问题。
// 例如,字符串 abcdef ,若要让def翻转到abc的前头,只要按照下述3个步骤操作即可:
// 1) 首先将原字符串分为两个部分,即X:abc,Y:def;
// 2) 将X反转,X->X^T,即得:abc->cba;将Y反转,Y->Y^T,即得:def->fed。
// 3) 反转上述步骤得到的结果字符串X^TY^T,即反转字符串cbafed的两部分(cba和fed)给予反转,
// cbafed得到defabc,形式化表示为(X^TY^T)^T=YX,这就实现了整个反转。
void leftShiftString(string& str, int shiftNum)
{
int str_len = str.length();
if (str_len == 0)
{
// 空串忽视
return;
}else
{
// 取模两个好处:一是避免不必要的移动,
// 二是防止index越界,尤其是shiftNum可能有负值,这时候要加上str_len避免取模返回负值
shiftNum = (shiftNum + str_len) % str_len;
}
if (shiftNum == 0)
{
//移动0位可以直接返回
return;
}
// 原字符串的X部分 -> X^T
reverseString(str, 0, shiftNum - 1);
cout << "X part: " << str << endl;
// 原字符串的Y部分 -> Y^T
reverseString(str, shiftNum, str_len - 1);
cout << "Y part: " << str << endl;
// (X^TY^T)^T -> YX
reverseString(str, 0, str_len - 1);
cout << "Both part: " << str << endl;
}
void reverseString(string& str, int from, int to)
{
while(from < to)
{
swap(str[from++], str[to--]);
}
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
//其他解法: 不是很通用,但是我自己想出来的,见
//http://blog.youkuaiyun.com/u013300875/article/details/44126909
//思路简单说就是我们知道str_len,也知道shiftNum,所以对于一个特定位置current的字符,
//它的目标位置target其实就确定了,我们只要把这它从current移动到target就行了
void rotate(string& str, int shiftNum)
{
int str_len = str.length();
if (str_len == 0)
{
// 空串忽视
return;
}
if (shiftNum == 0)
{
//移动0位可以直接返回
return;
}
//每个字符应该只被移动一次,也必须也被移动一次
//所以我们在 count == str_len 时停止
int count = 0;
for (int i = 0; i < str_len ; ++i)
{
int index = i;
char tmp = str[index];
while(1)
{
count++;
// 在index位置的字符将被移动到index+new
int index_new = (index - shiftNum + str_len) % str_len;
// 把index_new位置的元素保存到空出来的i位置
str[i] = str[index_new];
// 完成str[index] -> str[index_new]的移动
str[index_new] = tmp;
// 准备处理原来位于index_new的字符
tmp = str[i];
index = index_new;
// 如果我们已经处理了所有的字符,即完成了一个圈
// 我们是从i这个位置开始处理的,如果我们下一个要处理的又是i,
// 我们可以跳过它了
if (i == index)
{
break;
}
}
if (count == str_len)
{
break;
}
}
}
1.2 字符串包含
题目描述
给定两个分别由字母组成的字符串A和字符串B,字符串B的长度比字符串A短。请问,如何最快地判断字符串B中所有字母是否都在字符串A里?
为了简单起见,我们规定输入的字符串只包含大写英文字母,请实现函数bool StringContains(string &A, string &B)