字符串(p)review学习笔记

34 篇文章 0 订阅
31 篇文章 0 订阅

目前会持续填充一些字符串的模板,后续会对有关题目进行进一步展开。

【Hash】

一个比较万能的算法,在 Θ ( n ) \Theta(n) Θ(n)的预处理之后,就可以对两个字符串进行 Θ ( 常 数 ) \Theta(常数) Θ()的比较了。

其基本思想是将一串字符串映射成一个数字,通过比较数字来比较字符串。

具体上是将每位字母映射成一个值,每加进来一个值,就把前面的值乘上一个位数。这些数用前缀和存起来,需要比较时移位相减就可以了。

举个栗子,比如你现在有一个全是小写字母的字符串,你可以将所有小写字母一一对应:a->0,b->1,c->2,d->3…z->25。然后每次从后面加入一个新字母时就将原来的值乘上26就可以了。比如 b = 1 , b c = 1 ∗ 26 + 2 = 28 , b c d = 1 ∗ 26 ∗ 26 + 2 ∗ 26 + 3 = 731 b=1,bc=1*26+2=28,bcd=1*26*26+2*26+3=731 b=1,bc=126+2=28,bcd=12626+226+3=731
显然这样做数字很容易就会超过maxlongint,所以我们可以将得到的值对于一个大质数(使取模后的值尽可能均匀分布)取模(其实不取模让值自然溢出也可以不过很容易被卡)。可以证明当字符串长度不大时,出现相同值的概率是较低的
有时候我们会应对len<=1e6,query<=1e6的情况。为进一步降低冲突的概率,我们可以多Hash,同时对多个大质数取模,这时要注意常数因子带来的运行时间问题。
其实还有一种优化,我们可以取一个小质数基底,比如把原来的26进制换成29进制,然后取模,也可以使值均匀分布,而且不容易被毒瘤出题人卡掉。实际应用中,我一般会直接开longlong不取模,好像也没被卡过。

【模板】

给一个字符串,再有m个询问l1,r1,l2,r2问 [l1,r2] 和 [l2,r2] 的字符串是否相同。
保证Len<=1e6,n<=1e6
【code】

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int base=19;
const int maxn=1e6+100;
int n,m;
char s[maxn];
unsigned long long H[maxn],mul[maxn];
inline void read(int &x){
    x=0;int fl=1;char tmp=getchar();
    while(tmp<'0'||tmp>'9'){if(tmp=='-')fl=-fl;tmp=getchar();}
    while(tmp>='0'&&tmp<='9')x=(x<<1)+(x<<3)+tmp-'0',tmp=getchar();
    x=x*fl;
}
inline int query(int l,int r){
    return H[r]-H[l-1]*mul[r-l+1];
}
int main(){
    scanf("%s",s+1);n=strlen(s+1);
    mul[0]=1;
    for(int i=1;i<=n;i++) H[i]=H[i-1]*base+s[i]-'a'+1,mul[i]=mul[i-1]*base;
    cin>>m;
    for(int i=1;i<=m;i++){
        static int l1,r1,l2,r2;
        read(l1),read(r1),read(l2),read(r2);
        if(query(l1,r1)==query(l2,r2))puts("Yes");
        else puts("No");
    }
    return 0;
}

【manacher算法】

Θ ( n ) \Theta(n) Θ(n)的时间求以每个点为中心的最大回文串。(需要一定的想象力)

这是一个有对称中心的算法,如果出现"abba"的字符串就会变得很棘手。所以我们要预先进行预处理,将每个字母之间和两头插入一个一定不会出现在字符串中的字符。可以预见,插入后要求字符串一定为奇数长度,可以进行"马拉车"。

manacher算法也是利用了字符串中一个常用的思想——利用已知信息减少重复运算
我们需要的有一个

经过插入字符处理的字符串,s[]。
记录当点为中心的最长回文串的长度的数组,len[]。
记录目前为止最远所到达的右边界,mx。和mr的对称中心,pos。

对于一个len[i],len[i]>=min(mx-i,len[pos2-i])是一定正确的。
pic-1
如上图所示,如果由于在[pos
2-mx,mx]中的字符是对称的,所以当len[j]的左端点大于mx的对称点时,len[i]=len[j]。
反之,如果j的左端点超过或碰到mx的对称点那么len就至少为mr-i且有可能向右扩展。

整个算法for循环i一遍。mx从0右移至n一遍,所以总复杂就是 Θ ( n ) \Theta(n) Θ(n)

【模板】

给定一个字符串,求出其最长回文子串。
多个测试数据,每个测试数据一行,仅由小写字母组成的字符串。

【code】

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=1.1e7+1000;
char s[maxn<<1];int n;
int len[maxn<<1];
int main(){
    int kase=0;
    while(1){
        scanf("%s",s+1);n=strlen(s+1);
        if(s[1]=='E'&&s[2]=='N'&&s[3]=='D')break;
        for(int i=n<<1;i>=2;i-=2)s[i]=s[i>>1],s[i-1]='*';s[0]='*';
        n=(n<<1)+1,s[n]='*',s[n+1]=0;
        int pos=0,mr=0,ans=0;
        for(int i=1;i<=n;i++){
            if(mr>1)len[i]=min(mr-i,len[(pos<<1)-i]);
            else len[i]=1;
            while(s[i-len[i]]==s[i+len[i]])len[i]++;
            if(i+len[i]>mr){
                mr=i+len[i];
                pos=i;
            }
            ans=max(ans,len[i]-1);
        }
        printf("Case %d: %d\n",++kase,ans);
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值