剑指Offer(5)--替换空格
把字符串中出现的每个空格替换成“%20”。
#include<iostream>
using namespace std;
//假设string空间够进行替换
void RelaceString(char *string, int length)
{
if (string == nullptr || length < 1)
return;
int originalLength = 0;
int spaceNum = 0;
int i = 0;
while (string[i] != '\0')
{
++originalLength;
if (string[i] == ' ')
++spaceNum;
++i;
}
int newLength = originalLength + spaceNum * 2;
if (newLength < originalLength)
return;
//开始时0位置放的是正常的元素,originalLength位置放的'\0'
while (originalLength >= 0 && newLength>originalLength)
{
if (string[originalLength] == ' ')
{
string[newLength--] = '0';
string[newLength--] = '2';
string[newLength--] = '%';
}
else
string[newLength--] = string[originalLength];
--originalLength;
}
}
int main(void)
{
const int length = 100;
char str[length] = "hdas l d";
RelaceString(str, 100);
for (char i : str)
cout << i;
cout << endl;
system("pause");
return 0;
}
剑指Offer(19)--正则表达式匹配
请实现一个函数用来匹配包括'.'和'*'的正则表达式。模式中的字符'.'表示任意一个字符,而'*'表示它前面的字符可以出现任意次(包含0次)。 在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串"aaa"与模式"a.a"和"ab*ac*a"匹配,但是与"aa.a"和"ab*a"均不匹配。
思路:
其实就是针对表达式中的*和.以及正常的字符分开来考虑。
#include<iostream>
using namespace std;
bool matchCore(char *str, char *pattern);
bool match(char *string, char *pattern)
{
if (string == nullptr || pattern == nullptr)
return false;
return matchCore(string, pattern);
}
bool matchCore(char *str, char *pattern)
{
if (*str == '\0' && *pattern == '\0')
return true;
if (*str != '\0' && *pattern == '\0')
return false;
if (*(pattern + 1) == '*')
{
if (*pattern == *str || (*pattern == '.' && *str != '\0'))
{
return matchCore(str + 1, pattern + 2)//对应情况是*前面的字符只出现了一次
|| matchCore(str + 1, pattern)//对应情况是*前面的字符出现了多次或者 *前面出现了.
/*
这种情况也会在if中发生
char str1[] = "abc";
char str2[] = "a*abc";
*/
|| matchCore(str, pattern + 2);
}
else
{
return matchCore(str, pattern + 2);
}
}
if (*str == *pattern || (*pattern == '.' && *str != '\0'))
return matchCore(str + 1, pattern + 1);
return false;
}
int main(void)
{
char str1[] = "abc";
char str2[] = "a*abc";
bool result = match(str1, str2);
if (result)
cout << "1" << endl;
else
cout << "0" << endl;
system("pause");
return 0;
}
char firstNotRepeatChar(char *string)
{
if (string == nullptr)
return '\0';
int hashTable[256];
for (int i = 0; i < 256; ++i)
hashTable[i] = 0;
char *str = string;
while (*str != '\0')
{
hashTable[*(str++)]++;
}
str = string;
while (*str != '\0')
{
if (hashTable[*str] == 1)
return *str;
++str;
}
return '\0';
}
剑指Offer(20)--表示数值的字符串
请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。例如,字符串"+100","5e2","-123","3.1416"和"-1E-16"都表示数值。 但是"12e","1a3.14","1.2.3","+-5"和"12e+4.3"都不是。
思路
这道题还是比较简单的。表示数值的字符串遵循如下模式:
[sign]integral-digits[.[fractional-digits]][e|E[sign]exponential-digits]
其中,('['和']'之间的为可有可无的部分)。
在数值之前可能有一个表示正负的'+'或者'-'。接下来是若干个0到9的数位表示数值的整数部分(在某些小数里可能没有数值的整数部分)。如果数值是一个小数,那么在小数后面可能会有若干个0到9的数位表示数值的小数部分。如果数值用科学记数法表示,接下来是一个'e'或者'E',以及紧跟着的一个整数(可以有正负号)表示指数。
#include<iostream>
using namespace std;
bool scanInteger(char **str);
bool scanIntegerCore(char **str);
bool IsNumber(char *str)
{
if (str == nullptr)
return false;
bool numeric = scanInteger(&str);
//说明出现了小数部分
if (*str == '.')
{
++str;
//注意:小数点前面和后面均可以没有数字
numeric = numeric || scanIntegerCore(&str);
}
//说明接下来的数字应该是指数部分
if (*str == 'e' || *str == 'E')
{
++str;
//e或E的前面必须有数字,e或E的后面必须是整数
numeric = numeric && scanInteger(&str);
}
return numeric && *str == '\0';
}
bool scanInteger(char **str)
{
if (**str == '+' || **str == '-')
++(*str);
return scanIntegerCore(str);
}
bool scanIntegerCore(char **str)
{
char *before = *str;
while (**str != '\0' && **str > '\0' && **str <= '9')
++(*str);
//如果扫描到了数字,那么返回为true
return *str > before;
}
剑指Offer(46)--把数字翻译成字符串
给定一个数字,按照如下规则翻译成字符串:0翻译成“a”,1翻译成“b”…25翻译成“z”。一个数字有多种翻译可能,例如12258一共有5种,分别是bccfi,bwfi,bczi,mcfi,mzi。实现一个函数,用来计算一个数字有多少种不同的翻译方法。
递归版本
从头到末尾统计,会有大量的重复计算
f(i)=f(i+1)+g(i,i+1)*f(i+2);当第i位和第i+1位两位数字拼接起来的数字在10~25的范围内,函数g(i,i+1)为1,否则为0;
#include<iostream>
#include<string>
using namespace std;
int TranslationCore(const string& number, int i, const int length);
int Translation(int number)
{
if (number < 0)
return 0;
string s = to_string(number);
int length = s.length();
return TranslationCore(s,0,length);
}
int TranslationCore(const string& number, int i, const int length)
{
if (i == length - 1)
return 1;
int digit1 = number[i] - '0';
int digit2 = number[i + 1] - '0';
int num = digit1 * 10 + digit2;
//能和下一个结合
if (num >= 10 && num <= 25)
{
if (i == length - 2)
return 2;
else
return TranslationCore(number, i + 1, length) +
TranslationCore(number, i + 2, length);
}
else
{
//如果不能和下一个结合,那么结果还是下一个
return TranslationCore(number, i + 1, length);
}
}
int main(void)
{
int number = 12258;
int result = Translation(number);
cout << result << endl;
system("pause");
return 0;
}
非递归版本
从末尾到头开始统计,这样就不会重复计算
#include<iostream>
#include<string>
using namespace std;
int TranslationCore(const string& number, int i, const int length);
int Translation(int number)
{
if (number < 0)
return 0;
string s = to_string(number);
int length = s.length();
return TranslationCore(s,0,length);
}
int TranslationCore(const string& number, int i, const int 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 num = digit1 * 10 + digit2;
if (num >= 10 && num <= 25)
{
if (i < length - 2)
count += counts[i + 2];
else
count += 1;
}
}
counts[i] = count;
}
count = counts[0];
delete[]counts;
return count;
}
int main(void)
{
int number = 12258;
int result = Translation(number);
cout << result << endl;
system("pause");
return 0;
}
剑指Offer(48)--最长不重复字符的子字符串
思路:
从string由前到后进行统计,如果没有出现重复的字符,那么curLength就一直++;直到遇上一个先前已经出现过的字符停住进行判断,如果先前已经出现过的那个字符不在当前的curLength范围中,那么CurLength继续++,不受影响。如果出现在当前的curLength范围中,那么判断此时的这个curLength和maxLength的大小关系以进行更新,而且curLength也将从先前出现过的那个字符的后一个字符开始计算。
#include<iostream>
#include<string>
using namespace std;
int SubString(const string& str)
{
int curLength = 0;
int maxLength = 0;
int *posiotion = new int[26];
for (int i = 0; i < 26; ++i)
posiotion[i] = -1;
for (int i = 0; i < str.length(); ++i)
{
int prevIndex = posiotion[str[i] - 'a'];
if (prevIndex<0 || i - prevIndex>curLength)
++curLength;
else
{
if (curLength > maxLength)
maxLength = curLength;
curLength = i - prevIndex;
}
posiotion[str[i] - 'a'] = i;
}
if (curLength > maxLength)
maxLength = curLength;
delete[] posiotion;
return maxLength;
}
剑指Offer(58)--翻转字符串和左旋转字符串
翻转单词顺序。比如“I am a student” -> "student a am I"
思路:
第一步:翻转句子中的所有字符
第二步:翻转每个单词中字符的顺序
#include<iostream>
using namespace std;
void Reverse(char *begin, char *end)
{
while (begin < end)
{
char temp = *begin;
*begin = *end;
*end = temp;
++begin;
--end;
}
}
char *ReverseString(char *string)
{
if (string == nullptr)
return nullptr;
char *m_begin = string;
char *m_end = string;
while (*m_end != '\0')
++m_end;
--m_end;
//翻转整个句子
Reverse(m_begin, m_end);
m_begin = m_end = string;
while (*m_begin != '\0')
{
if (*m_begin == ' ')
{
++m_begin;
++m_end;
}
else if (*m_end == ' ' || *m_end == '\0')
{
Reverse(m_begin, --m_end);
m_begin = ++m_end;
}
else
{
++m_end;
}
}
return string;
}
左旋转字符串
输入字符串abcdefg和2,得到cdefgab
思路:
以n为分割点,先旋转前半部分
再旋转后半部分
最后旋转整个字符串
#include<iostream>
using namespace std;
void Reverse(char *begin, char *end)
{
while (begin < end)
{
char temp = *begin;
*begin = *end;
*end = temp;
++begin;
--end;
}
}
char *LeftRotateString(char *string, int n)
{
if (string == nullptr || n < 0)
return nullptr;
int length = 0;
char *str = string;
while (*str != '\0')
{
++length;
++str;
}
if (n > length)
return nullptr;
char *firstStart = string;
char *firstEnd = string + n - 1;
char *secondStart = string + n;
char *secondEnd = --str;
//翻转字符串的前面n个字符
Reverse(firstStart, firstEnd);
//翻转字符串的后面部分
Reverse(secondStart, secondEnd);
//翻转整个字符串
Reverse(firstStart, secondEnd);
return string;
}
剑指Offer(50)--第一个只出现一次的字符
思路:定义哈希表的键值是字符,而值是该字符出现的次数。然后从头开始扫描字符串两次。第一次扫描字符串时,每扫描到一个字符,就在哈希表的对应项中把次数加1.接下来第二次扫描时,每扫描到一个字符,就从哈希表中得到该字符出现的次数。这样,第一个只出现一次的字符就是符合要求的输出。
char FirstNotRepeatingChar(const char* string)
{
if (string == nullptr)
return '\0';
const int tableSize = 256;
int hashTable[tableSize];
for (int i = 0; i < tableSize; ++i)
hashTable[i] = 0;
const char* pHashKey = string;
while (*(pHashKey) != '\0')
hashTable[*(pHashKey++)] ++;
pHashKey = string;
while (*pHashKey != '\0')
{
if (hashTable[*pHashKey] == 1)
return *pHashKey;
pHashKey++;
}
return '\0';
}
int main(void)
{
char string[] = "yaabc";
char result = FirstNotRepeatingChar(string);
cout << result << endl;
system("pause");
return 0;
}
#include<iostream>
using namespace std;
char firstNotRepeatChar(char *string)
{
if (string == nullptr)
return '\0';
int hashTable[256];
for (int i = 0; i < 256; ++i)
hashTable[i] = -1;
char *str = string;
int index = 0;
while (*str != '\0')
{
//如果某个字符是第一次出现,赋予以它为下标的值为index(越小代表这个字符越早出现)
if (hashTable[*str] == -1)
hashTable[*str] = index;
//如果某个字符不是第一次出现,赋予以它为下标的值为-2
else if (hashTable[*str] >= 0)
hashTable[*str] = -2;
++ str;
++index;
}
str = string;
//表示一个很大的数
int minIndex = 0x7FFFFFFF;
char ch = '\0';
for (int i = 0; i < 256; ++i)
{
if (hashTable[i] > 0 && hashTable[i] < minIndex)
{
ch = (char)(i);
minIndex = hashTable[i];
}
}
return ch;
}
int main(void)
{
char string[] = "ababckc";
char result = firstNotRepeatChar(string);
cout << result << endl;
system("pause");
return 0;
}
注意:
#include<iostream>
using namespace std;
/*
下面这个代码说明,如果形参是指针,那么在函数中
经过对指针的操作之后,发生变化的是指针指向的位置
的值,而指针所指的位置不会变化,比如下面的str++,
在函数返回时并未发生移动
*/
void fun1(char *str)
{
*str = 'k';
str++;
}
void fun2(char *str)
{
char *str1 = str;
cout << *str << endl;
fun1(str);
cout << *str << endl;
}
int main(void)
{
char string[] = "abc";
fun2(string);
system("pause");
return 0;
}