第【5】章: 字符串 学习报告
(5.1)节 判断字符串有无重复元素
1、题干:
2、解答思路:在ascii码中,字符对应的int是0-127,所以创建128大小的数组,对原字符串进行计数,然后遍历数组,如果出现大于1的说明出现了两次以上,有重复。
如不能创建空间,那么使用比较查找,遍历每一个元素,在后面字符串查找,找到了说明重复。O(n(n-1)/2)=O(n^2).优化可以先进行排序后再比较相邻两个字符是否相等;O(nlgn+n)
3、关键代码
int text(char a[],int t)//t为字符的个数 ,加末尾空字符
{int d[128];
for(int i=0;i<128;i++)//建立128大小的数组,初始化0
d[i]=0;
for(int i=0;i<t;i++)//如不比较最后那个自带的,可以t-1
{
d[a[i]]++;
}
for(int i=0;i<128;i++)
if(d[i]>1)
return 1;
return 0;
}
(5.2)节 翻转字符串
1、题干:
将字符串倒过来;
2、解答思路: 创建一个与字符串一样长度的字符数组,将源字符串从后面开始—遍历的值赋予新的数组++;
3、关键代码:
void text(char a[],int t)//t为字符的个数 ,加末尾空字符
{
char r[t+1];
int j=0;
for(int i=t-2;i>=0;i--)
{r[j]=a[i];
j++;}
for(int i=0;i<t-1;i++)
a[i]=r[i];
}
(5.3)节 变形词问题
1、题干:判断,两个字符串能否变成相互演变
2、解答思路:首先这两个字符串必须有相同长度,其次要有相同的元素,跟第一题差不多,创建128大小的数组,轮流对两个字符串进行计数,如果出现奇数,则,两个字符串有不同的元素,则不能变形。
3、关键代码:#includ<cstring> strlen()获取字符串长度;
int text(char a[],char b[])
{
int r[128];
for(int i=0;i<128;i++)
r[i]=0;
if(strlen(a)!=strlen(b))
return 0;
for(int i=0;i<strlen(a);i++)
{
r[a[i]]++;r[b[i]]++;
}
for(int i=0;i<128;i++)
if(r[i]&1)///当r[i]为奇数时为真,则不能变形返回0
return 0;
return 1;
}
(5.4)节 替换掉字符串中的空格
1、题干:在原字符串中,选择将要替换掉的子字符串进行替换;
2、解答思路:用字符数组建立的字符串有诸多的限制;C++为我们提供了string类,可以方便我们的操作
3、关键代码
string text(string a,string b,string c)//将a字符串中的所有b子字符串替换成c
{
int count=0;//子字符串的个数
int t=0;
string r;
for(int i=0;i<a.size();i++ ) //count统计个数
{
while(a[i]==b[t])//t表示循环的次数
{
if(t==b.size()-1)
{r+=c;
count++;
i+=b.size();
break;}
i++;t++;
}
i-=t;t=0;
r+=a[i];
}
if(count==0)
return "替换失败,找不到子字符串";
return r;//返回替换好的字符串
}
(5.5)节 压缩字符串
1、题干:
2、解答思路:对字符串进行遍历计数, 然后再根据数量生成新的字符串
利用stringstream:使用stringstream的时候要注意加#include"sstream"。比如说我要把int类型的23转为string类型,那么我可以这样实现:
- int a = 23;
- stringstream ss;
- ss << a;
- string s1 = ss.str();
3、关键代码:
#include<sstream>
string text(string a)
{
int r[128];
for(int i=0;i<128;i++)//初始化
r[i]=0;
for(int i=0;i<a.size();i++)
r[a[i]]++;
string str;
for(int i=0;i<128;i++)
{
if(r[i]>0)
{str+=i;
stringstream t;///
t<<r[i];//将int转换成string
str+=t.str();
}
}
if(str.length()<a.length())//若压缩后比原来还长则返回原来
return str;
else return a;
}
(5.7)节 旋转词
1、题干:
2、解答思路:要判断a是否可以由b旋转后得到。可以将把字符串b加上他本身,如果可以的话,那么a就在于两个b的交界处;
3、关键代码:
int text(string a,string b)
{
string str=b+b;
int j=0;
for(int i=0;i<str.size();i++)
{
while(a[j]==str[i])
{if(j==a.size()-1)
return 1;
i++;j++;
}
i-=j;j=0;
}
return 0;
}
(5.8)按单词翻转
1、题干
比如 That is my home,翻转后就是tahT si ym emoh
2、解答思路:设置边界,扫描到空格为一个单词,设置两个指针表示一个单词,进行翻转
3、关键代码:
略
(5.9)去掉连续出现的k个0
1、题干:
去掉字符串中k个连续出现的0
2、解答思路:遍历字符串,建立两个指针指定0的区间,将区间的大小对k进行取余,余数为该区间剩余的0的个数;
3、关键代码:
string text(string a,int k)
{
int L=0;
int count=0;
string str;
for(int i=0;i<a.size();i++)
{
while(a[i]==48)//0的 ASCII码序号为48;
{i++;count++;}
while (count%k)
{count--;str+=48; }
str+=a[i];
}
return str;
}
(5.10)神奇的回文串
1、题干:字符串是否为回文字符串
2、解答思路:回文字符串,正着念和反着念是一样的。所以求出字符串的逆序列,两个字符串如果一样就返回true
3、关键代码
略
(5.11)节 最短按摘要生成
1、题干
2、解答思路:暴力破解,找出所有关键字的所有位置,记录起来,通过比较找出包含所有关键字的最短的子字符串
3、关键代码:略
(5.12)节
(5.13)节 字符串匹配 Rabinkarp
1、题干:
2、解答思路:通过对子串求出哈希值,然后对源字符串每K个连续的字符串进行求哈希值(滚动哈希),并存储在数组里,然后与子串的哈希值比较。
3、关键代码:
long hash(string s,int begin,int end,int hc)///在原串中,从begin开始end个字符获取参数为hc的哈希值
{int hash=0;
for(int i=0;i<end;i++)
hash=(hash*hc+s[i]);
return hash;
}
int st(int x,int n)//求x的n次方
{
for(int i=1;i<n;i++)
x*=x;
return x;
}
void haout(string a,string b,int hc)
{
long hsz[a.size()-b.size()+1];///存储a源字符串所有b一样长度的哈希值
if(a.size()<b.size())
{cout<<"错误,源字符串比子串短";
return;
}
int bhs=hash(b,0,b.size(),hc);///b的哈希值
hsz[0]=hash(a,0,b.size(),hc);
for(int i=1;i<=a.size()-b.size();i++)
//将a的b长度哈希值 存储在haz数组中,下标表示a中子串的起始位置
{// 前一个哈希值*hc减去第一个,然后再加上新的一个
hsz[i]=(hsz[i-1]*hc-a[i-1]*st(hc,b.size()))+a[i+b.size()-1];
}
for(int i=0;i<=a.size()-b.size();i++)
if(hsz[i]==bhs)///找出哈希值与b相同的串第一个元素的下标
//哈希值在某种程度会出现哈希误差,即不同的子串计算出相同的哈希值
//因此可以在找到相同的哈希值是进行朴素验证,即一个个比较
cout<<i<<endl;
}
(5.14)节
(5.15)节 字符串匹配 KMP
1、题干:
2、解答思路:通过子串建立next数组,优化暴力匹配,原数组循循渐进,子数组不匹配时返回next数组中指向的位置进行匹配
3、关键代码:
int tet(string s,string a)///建立next数组
{
if(a.size()>s.size())
return -1;
int next[a.size()];
next[0]=-1;
next[1]=0;
int j=1;
int k=next[j];
while(j<a.size()-1)
if(k<0||a[j]==a[k])
next[++j]=++k;
else
k=next[k];
///////////////////////查找
j=0;
int i=0;
while(i<s.size())
{
if(j==-1||s[i]==a[j])
{j++;i++;
}
else
j=next[j];
if(j==a.size())
return (i-j); ///返回第一个位置
}
return -1;
}
(5.16)节
(5.17)节
(5.18)节 后缀数组
1、题干:子串一定是某个后缀的前缀
2、解答思路:
3、关键代码:
建立后缀数组:
string tet(string s,int i)
{
string a;
for(int j=i;j<s.size();j++)
a+=s[j]; ///创建后缀
return a;
}
string b="bababb";///原数组
string a[b.size()];//存后缀的数组
for(int i=0;i<b.size();i++)
a[i]=tet(b,i);//建立后缀数组