剑指Offer中一共有7道关于字符串的面试题
面试题5:替换空格
题目描述:请实现一个函数,将一个字符串中的每个空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy.。
思路:
JS
function replaceSpace(str)
{
return str.replace(/\s/g,'%20')
}
C++
#include <cstdio>
#include <cstring>
/*length 为字符数组str的总容量,大于或等于字符串str的实际长度*/
void ReplaceBlank(char str[], int length)
{
if(str == nullptr && length <= 0)
return;
/*originalLength 为字符串str的实际长度*/
int originalLength = 0;
int numberOfBlank = 0;
int i = 0;
while(str[i] != '\0')
{
++ originalLength;
if(str[i] == ' ')
++ numberOfBlank;
++ i;
}
/*newLength 为把空格替换成'%20'之后的长度*/
int newLength = originalLength + numberOfBlank * 2;
if(newLength > length)
return;
int indexOfOriginal = originalLength;
int indexOfNew = newLength;
while(indexOfOriginal >= 0 && indexOfNew > indexOfOriginal)
{
if(str[indexOfOriginal] == ' ')
{
str[indexOfNew --] = '0';
str[indexOfNew --] = '2';
str[indexOfNew --] = '%';
}
else
{
str[indexOfNew --] = str[indexOfOriginal];
}
-- indexOfOriginal;
}
}
面试题20:表示数值的字符串
题目描述:请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。例如,字符串"+100","5e2","-123","3.1416"和"-1E-16"都表示数值。 但是"12e","1a3.14","1.2.3","+-5"和"12e+4.3"都不是。
思路:
#include <stdio.h>
bool scanUnsignedInteger(const char** str);
bool scanInteger(const char** str);
// 数字的格式可以用A[.[B]][e|EC]或者.B[e|EC]表示,其中A和C都是
// 整数(可以有正负号,也可以没有),而B是一个无符号整数
bool isNumeric(const char* str)
{
if(str == nullptr)
return false;
bool numeric = scanInteger(&str);
// 如果出现'.',接下来是数字的小数部分
if(*str == '.')
{
++str;
// 下面一行代码用||的原因:
// 1. 小数可以没有整数部分,例如.123等于0.123;
// 2. 小数点后面可以没有数字,例如233.等于233.0;
// 3. 当然小数点前面和后面可以有数字,例如233.666
numeric = scanUnsignedInteger(&str) || numeric;
}
// 如果出现'e'或者'E',接下来跟着的是数字的指数部分
if(*str == 'e' || *str == 'E')
{
++str;
// 下面一行代码用&&的原因:
// 1. 当e或E前面没有数字时,整个字符串不能表示数字,例如.e1、e1;
// 2. 当e或E后面没有整数时,整个字符串不能表示数字,例如12e、12e+5.4
numeric = numeric && scanInteger(&str);
}
return numeric && *str == '\0';
}
bool scanUnsignedInteger(const char** str)
{
const char* before = *str;
while(**str != '\0' && **str >= '0' && **str <= '9')
++(*str);
// 当str中存在若干0-9的数字时,返回true
return *str > before;
}
// 整数的格式可以用[+|-]B表示, 其中B为无符号整数
bool scanInteger(const char** str)
{
if(**str == '+' || **str == '-')
++(*str);
return scanUnsignedInteger(str);
}
面试题38:字符串的排列
题目描述:输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。
思路:
#include <cstdio>
void Permutation(char* pStr, char* pBegin);
void Permutation(char* pStr)
{
if(pStr == nullptr)
return;
Permutation(pStr, pStr);
}
void Permutation(char* pStr, char* pBegin)
{
if(*pBegin == '\0')
{
printf("%s\n", pStr);
}
else
{
for(char* pCh = pBegin; *pCh != '\0'; ++ pCh)
{
char temp = *pCh;
*pCh = *pBegin;
*pBegin = temp;
Permutation(pStr, pBegin + 1);
temp = *pCh;
*pCh = *pBegin;
*pBegin = temp;
}
}
}
面试题46:把数字翻译成字符串
题目描述:给定一个数字,我们按照如下规则把它翻译成字符串:0翻译成‘a’,1翻译成‘b’,......,11翻译成‘l’,......,25翻译成‘z’。一个数字可能有多个翻译。例如,12258有5种不同的翻译,分别是“bccfi”,“bwfi”,“bczi”,“mcfi”和“mzi”。请编程实现一个函数,用来计算一个数字有多少种不同的翻译方法。
思路:
#include <string>
#include <iostream>
using namespace std;
int GetTranslationCount(const string& number);
int GetTranslationCount(int number)
{
if(number < 0)
return 0;
string numberInString = to_string(number);
return GetTranslationCount(numberInString);
}
int GetTranslationCount(const string& number)
{
int length = number.length();
int* counts = new int[length];
int count = 0;
for(int i = length - 1; i >= 0; --i)
{
count = 0;
if(i < length - 1)
count = counts[i + 1];
else
count = 1;
if(i < length - 1)
{
int digit1 = number[i] - '0';
int digit2 = number[i + 1] - '0';
int converted = digit1 * 10 + digit2;
if(converted >= 10 && converted <= 25)
{
if(i < length - 2)
count += counts[i + 2];
else
count += 1;
}
}
counts[i] = count;
}
count = counts[0];
delete[] counts;
return count;
}
面试题48:最长不含重复字符的子字符串
题目描述:输出子字符串的长度。例如,在字符串“arabcacfr”中,最长的不含重复字符的子字符串是“acfr”,长度为4。
思路:
// 动态规划
int longestSubstringWithoutDuplication_2(const std::string& str)
{
int curLength = 0;
int maxLength = 0;
int* position = new int[26];
for(int i = 0; i < 26; ++i)
position[i] = -1;
for(int i = 0; i < str.length(); ++i)
{
int prevIndex = position[str[i] - 'a'];
if(prevIndex < 0 || i - prevIndex > curLength)
++curLength;
else
{
if(curLength > maxLength)
maxLength = curLength;
curLength = i - prevIndex;
}
position[str[i] - 'a'] = i;
}
if(curLength > maxLength)
maxLength = curLength;
delete[] position;
return maxLength;
}
面试题50:第一次只出现一次的字符
题目一:字符串中第一个只出现一次的字符
题目描述:如输入“abaccdeff”,则输出‘b’。
思路:哈希表
#include <cstdio>
#include <string>
char FirstNotRepeatingChar(const char* pString)
{
if(pString == nullptr)
return '\0';
const int tableSize = 256;
unsigned int hashTable[tableSize];
for(unsigned int i = 0; i < tableSize; ++i)
hashTable[i] = 0;
const char* pHashKey = pString;
while(*(pHashKey) != '\0')
hashTable[*(pHashKey++)] ++;
pHashKey = pString;
while(*pHashKey != '\0')
{
if(hashTable[*pHashKey] == 1)
return *pHashKey;
pHashKey++;
}
return '\0';
}
题目二:字符流中第一个只出现一次的字符
题目描述:请实现一个函数用来找出字符流中第一个只出现一次的字符。例如,当从字符流中只读出前两个字符"go"时,第一个只出现一次的字符是"g"。当从该字符流中读出前六个字符“google"时,第一个只出现一次的字符是"l"。
思路:哈希表
#include <cstdio>
#include <vector>
#include <limits>
using namespace std;
class CharStatistics
{
public:
CharStatistics() : index(0)
{
for(int i = 0; i < 256; ++i)
occurrence[i] = -1;
}
void Insert(char ch)
{
if(occurrence[ch] == -1)
occurrence[ch] = index;
else if(occurrence[ch] >= 0)
occurrence[ch] = -2;
index++;
}
char FirstAppearingOnce()
{
char ch = '\0';
int minIndex = numeric_limits<int>::max();
for(int i = 0; i < 256; ++i)
{
if(occurrence[i] >= 0 && occurrence[i] < minIndex)
{
ch = (char) i;
minIndex = occurrence[i];
}
}
return ch;
}
private:
// occurrence[i]: A character with ASCII value i;
// occurrence[i] = -1: The character has not found;
// occurrence[i] = -2: The character has been found for mutlple times
// occurrence[i] >= 0: The character has been found only once
int occurrence[256];
int index;
};
面试题58:翻转字符串
题目一:翻转单词顺序
题目描述:牛客最近来了一个新员工Fish,每天早晨总是会拿着一本英文杂志,写些句子在本子上。同事Cat对Fish写的内容颇感兴趣,有一天他向Fish借来翻看,但却读不懂它的意思。例如,“student. a am I”。后来才意识到,这家伙原来把句子单词的顺序翻转了,正确的句子应该是“I am a student.”。Cat对一一的翻转这些单词顺序可不在行,你能帮助他么?
思路:
#include <cstdio>
#include "..\Utilities\StringUtil.h"
#include <string>
char* ReverseSentence(char *pData)
{
if(pData == nullptr)
return nullptr;
char *pBegin = pData;
char *pEnd = pData;
while(*pEnd != '\0')
pEnd ++;
pEnd--;
// 翻转整个句子
Reverse(pBegin, pEnd);
// 翻转句子中的每个单词
pBegin = pEnd = pData;
while(*pBegin != '\0')
{
if(*pBegin == ' ')
{
pBegin ++;
pEnd ++;
}
else if(*pEnd == ' ' || *pEnd == '\0')
{
Reverse(pBegin, --pEnd);
pBegin = ++pEnd;
}
else
pEnd ++;
}
return pData;
}
void Reverse(char *pBegin, char *pEnd)
{
if(pBegin == nullptr || pEnd == nullptr){
return
while(pBegin < pEnd)
{
char temp = *pBegin;
*pBegin = *pEnd;
*pEnd = temp;
pBegin++;
pEnd--;
}
}
题目二:左旋转字符串
题目描述:汇编语言中有一种移位指令叫做循环左移(ROL),现在有个简单的任务,就是用字符串模拟这个指令的运算结果。对于一个给定的字符序列S,请你把其循环左移K位后的序列输出。例如,字符序列S=”abcXYZdef”,要求输出循环左移3位后的结果,即“XYZdefabc”。是不是很简单?OK,搞定它!
思路:
#include <cstdio>
#include "..\Utilities\StringUtil.h"
#include <string.h>
char* LeftRotateString(char* pStr, int n)
{
if(pStr != nullptr)
{
int nLength = static_cast<int>(strlen(pStr));
if(nLength > 0 && n > 0 && n < nLength)
{
char* pFirstStart = pStr;
char* pFirstEnd = pStr + n - 1;
char* pSecondStart = pStr + n;
char* pSecondEnd = pStr + nLength - 1;
// 翻转字符串的前面n个字符
Reverse(pFirstStart, pFirstEnd);
// 翻转字符串的后面部分
Reverse(pSecondStart, pSecondEnd);
// 翻转整个字符串
Reverse(pFirstStart, pSecondEnd);
}
}
return pStr;
}
END