时隔多年,我又来写总结啦!!!
字符串string用法+算法总结
先总述一下
字符串(string)是一种常用的数据结构,存储方式类似于数组,c++里规定其存储的起始下标为0,通常用
l
e
n
=
s
.
s
i
z
e
(
)
len=s.size()
len=s.size()来表示字符串长度。
另外,字符串也可以char数组的方式实现。此时需用len=strlen(s)求其长度
用法
c++中,string自带的用法有很多,此处仅介绍几个较为初级的、常用的用法。
(注:char数组没有以下用法)
插入
string支持最基础的+=插入。
string s="abcd";
char ch='e';
s+=ch;
//此时s="abcde"
下面列举 i n s e r t ( ) insert() insert()函数的具体用法
string s="abcd",ch="wxyz";
s.insert(2,ch);//在字符串s的位置2上插入字符串ch,此时s="abwxyzcd"
s.inzert(2,ch,1,3);//在字符串s的2的位置上插入字符串ch中以位置1为开头,长度为3的字符串,此时s="abxyzcd"
char ch1='x';
s.insert(2,3,ch1);//在字符串s的位置2上插入3个字符ch1,此时s="abxxxcd"
删除
string s="abcdefg";
s.erase(2,4);//删除字符串s的以位置2为开头、长度为4的子串,此时s="abg"
替换
string s="abcdefg",ch="xyz";
s.replace(2,4,ch);//将字符串s的以位置2为开头,长度为4的子串换成字符串ch,此时s="abxyzg"
用法到此结束。
算法
引入hash
hash表,又称散列表,一般由hash函数和链表结构共同实现。
hash算法的本质是建立映射关系,其他类似的算法还有离散化和 m a p ( 数 据 结 构 ) map(数据结构) map(数据结构)。hash的思想是取一个 p p p值,将数字 x x x存在 h e a d head head数组中 x m o d p x\mod p xmodp的格子里。但是这显然会有冲突(既必定存在多个 x x x,使得 x m o d p x\mod p xmodp的值相等)。处理这种冲突的最常用方法为拉链法——开一个链表结构(类似于图论中的链式前向星)。具体写法见code:
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
int a[maxn],head[maxn]/*存储的格子*/;
int A/*数组长度*/,p/*为了达到效果,p要选大于A的质数*/,tot=0;
struct star {//链式前向星
int nxt,val;
};
star h[maxn];
bool check(int x) {//找质数
if(x==1) return 0;
if(x==2) return 1;
if(x%2==0) return 0;
for(int j=3;j<=sqrt(x);j+=2)
if(x%j==0) return 0;
return 1;
}
void add(int x) {
int k=(x%p+p)%p;//数字x的hash值,这么写是为了避免x<0的情况
h[++tot].val=x;//数字原来的值
h[tot].nxt=head[k];//数字在格子里的位置
head[k]=tot;//这个格子最后一次存入数字是在第tot个前向星中
}
bool Find(int x) {//查找数字x是否出现过
for(int j=head[x%p];j!=-1;j=h[j].nxt)
if(h[j].val==x) return 1;//出现过
return 0;
}
int main() {
scanf("%d",&A);
for(int i=1;i<=A;i++)
scanf("%d",&a[i]);
memset(head,-1,sizeof(head));
for(p=A+1;;p++)
if(check(p)) break;
for(int i=1;i<=A;i++)
add(a[i]);//插入
return 0;
}
hash算法优于离散化和map的地方在于它的时间复杂度是严格O(n)的。在忽略常数的情况下
字符串hash
普通的hash算法针对的是数字,无法存储字符串。这个算法就是针对字符串而出现的。
对于一个字符串
s
s
s,它的hash值求法是:将
s
s
s中的每个字符都转换成一个数字,然后选取一个数
b
a
s
e
base
base为进制数,将
s
s
s转换位一个
b
a
s
e
base
base进制数的数字。为了方便,我们一般将字符对应的ASCLL码作为其转换的数字,进制数一般取131(因为小写字母z的ASCLL码是所有大小写字母中最大的,为126)。因为这个数字过大,所以我们要进行取模运算。又因为取模运算常数很大,所以我们引入一个unsigned long long(无符号64位整型,范围0~264-1)。当数字大于
0
∼
0 \sim
0∼ 264
−
1
-1
−1时,它会自动对264取模,而且常数很小(可以想象成位运算)。
所以,一般查找这个字符串有没有出现过时,仅靠字符串hash是不够的,需要搭配数字hash共同实现。code如下:
#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;
const int maxn=50050;
int mod=50021;
struct Hash {//数字hash(见上文)
int nxt;
ull val;
};
Hash edge[maxn];
int p=131,n;
int head[maxn],tot=0;
ull Get(string s) {//字符串hash值
ull num=0;
for(int j=0;j<s.size();j++)
num=num*p+(ull)(s[j]-'a'+1);
return num;
}
int H(ull x) {
return (x%mod+mod)%mod;
}
bool add(ull x) {
int k=H(x);
for(int j=head[k];j!=-1;j=edge[j].nxt)
if(edge[j].val==x) return 1;//出现过
edge[++tot].val=x;//未出现,插入
edge[tot].nxt=head[k];
head[k]=tot;
return 0;
}
int main() {
scanf("%d",&n);
memset(head,-1,sizeof(head));
for(int i=1;i<=n;i++) {
string x;
cin>>x;
ull res=Get(x);//得出字符串x的hash值
if(add(res)/*查找res这个数字有没有出现过,如果没有就插入*/)
printf("%d\n",i);
}
return 0;
}
现在最基础的字符串hash我们已经了解。通过字符串hash我们还可完成更多操作。我们先引入几个公式:
H(S):字符串S的hash值
H(S+T)=H(S)*p^length(T)+H(T)
H(T)=H(S+T)-H(S)*p^length(T)
通过这三个公式,我们即可在O(n)的时间复杂度内求出字符串S的hash值,并在O(1)的时间复杂度内求出S的任意子串的hash值。例题:兔子与兔子
code:
#include<bits/stdc++.h>
using namespace std;
string s;
const int maxn=1e6+10;
typedef unsigned long long ull;
ull f[maxn],q[maxn];
int p=131;
void Prepare() {
for(int i=0;i<s.size();i++) {
if(i==0) f[i+1]=(ull)(s[i]-'a'+1);
else f[i+1]=f[i]*p+(ull)(s[i]-'a'+1);
if(i==0) q[i]=1;
else q[i]=q[i-1]*p;
}
}
int main() {
cin>>s;
Prepare();//O(n)求s各个位置的hash值
int m;
scanf("%d",&m);
for(int i=1;i<=m;i++) {
int l1,l2,r1,r2;
scanf("%d%d%d%d",&l1,&l2,&r1,&r2);
//判断区间[l1,l2]与[r1,r2]是否相等
ull h1=f[l2]-f[l1-1]*q[l2-l1+1];
ull h2=f[r2]-f[r1-1]*q[r2-r1+1];
if(h1==h2) puts("Yes");
else puts("No");
}
return 0;
}
KMP模式匹配
待更
最小表示法
待更