字符串

作者: 云中翻月 | 来源:发表于2019-09-28 20:19 被阅读0次

字符串DP

字符串匹配

后缀数组

后缀数组的注意点:
1 两个串拼接在一起。
2 height数组的性质。
POJ 1509: Glass Beads
字符串最小表示法模板题。
代码如下

/*

*/
#define method_1
#ifdef method_1
/*
字符串最小表示法模板题。 
*/
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<vector>
#include<string> //不要用cstring 否则会在POJ上CE 
#include<cstdlib>
using namespace std;
typedef long long ll;
const int maxn=2000000+5;
const int INF=0x3f3f3f3f;
int T,n;
char s[maxn];
int cal(char* s) {
    int i=0,j=1,k;
    while(i<n&&j<n) {
        for(k=0; k<n&&s[i+k]==s[j+k]; k++);
        if(k==n) break;
        if(s[i+k]>s[j+k]) {
            i=i+k+1;
            if(i==j) i++;
        } else {
            j=j+k+1;
            if(i==j) j++;
        }
    }
    return min(i,j);
}
int main() {
    ios::sync_with_stdio(false);
    //freopen("Glass Beads.in","r",stdin);
    cin>>T;
    while(T--){
        cin>>s;
        n=strlen(s);
//      cout<<n<<endl;
        for(int i=0;i<=n-1;i++) s[i+n]=s[i];
//      for(int i=0;i<=2*n-1;i++) cout<<s[i];
//      cout<<endl;
        cout<<cal(s)+1<<endl;
    }
    return 0;
}
#endif
#ifdef method_2
/*

*/

#endif
#ifdef method_3
/*

*/

#endif

POJ 3415: Common Substrings
题意:A与B长度至少为k的公共子串个数。
题目相当于需要求A的所有后缀和B的所有后缀之间的最长公共前缀的长度,并把最长公共前缀长度不小于K的部分全部加起来。
首先将A和B拼起来(中间放一个不可能出现的字符),然后对这个新字符串预处理出sa和h数组。
接下来,对于属于A的后缀i和属于B的后缀j的lcp(h[i+1]到h[j]的最小值),对答案的贡献就是lcp-K+1。
显然,直接枚举i和j的时间复杂度是O(n^{2})
考虑优化。
假设目前处理到属于B的后缀j,那么就需要统计与前面A的后缀能够组成多少长度不小于K的公共子串。
我们设前面满足条件的A的后缀的个数为cnt,显然lcp的瓶颈就是后缀j和前面cnt个A的后缀对应的h值中的最小值。
因为j的枚举过程为从左向右,所以上述所求的h的值需要满足单调递减的,于是可以用单调栈来维护这个值。
具体地说,我们维护一个h值单调递增的栈,每加入一个属于B的后缀j,就将栈中前面h值大于h[j]的部分删掉。这些删掉的部分就会和后缀j组成lcp=h[j]的数对,从而计算贡献。
对于A的后缀,同样的方法处理一遍即可。
需要注意的是:
1 只有需要统计的(属于不同串)一种后缀的height是有价值的,不需要统计的一种后缀的height需要入栈,但是没有价值。
2 为了保证贡献始终为正数,需要按照h分组,每一组中的h都要大于等于k(因为贡献取决于组内的最小值,所以可以将一组元素全部压缩成一个元素),这样组内产生的数对之间的最小h就一定大于等于k。按照每一块统计即可。
3 不要忘记开longlong。
其他题解链接:https://blog.csdn.net/u013368721/article/details/41852771
代码如下

/*

*/
#define method_1
#ifdef method_1
/*
题意:A与B长度至少为k的公共子串个数。 
题目相当于需要求A的所有后缀和B的所有后缀之间的最长公共前缀的长度,并把最长公共前缀长度不小于K的部分全部加起来。
首先将A和B拼起来(中间放一个不可能出现的字符),然后对这个新字符串预处理出sa和h数组。
接下来,对于属于A的后缀i和属于B的后缀j的lcp(h[i+1]到h[j]的最小值),对答案的贡献就是lcp-K+1。
显然,直接枚举i和j的时间复杂度是O(n^{2})。
考虑优化。
假设目前处理到属于B的后缀j,那么就需要统计与前面A的后缀能够组成多少长度不小于K的公共子串。
我们设前面满足条件的A的后缀的个数为cnt,显然lcp的瓶颈就是后缀j和前面cnt个A的后缀对应的h值中的最小值。
因为j的枚举过程为从左向右,所以上述所求的h的值需要满足单调递减的,于是可以用单调栈来维护这个值。
具体地说,我们维护一个h值单调递增的栈,每加入一个属于B的后缀j,就将栈中前面h值大于h[j]的部分删掉。这些删掉的部分就会和后缀j组成lcp=h[j]的数对,从而计算贡献。 
对于A的后缀,同样的方法处理一遍即可。
需要注意的是:
1 只有需要统计的(属于不同串)一种后缀的height是有价值的,不需要统计的一种后缀的height需要入栈,但是没有价值。 
2 为了保证贡献始终为正数,需要按照h分组,每一组中的h都要大于等于k(因为贡献取决于组内的最小值,所以可以将一组元素全部压缩成一个元素),这样组内产生的数对之间的最小h就一定大于等于k。按照每一块统计即可。 
3 不要忘记开longlong。 
其他题解链接:https://blog.csdn.net/u013368721/article/details/41852771 
*/
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<vector>
#include<cstring>
#include<cstdlib>
#include<iomanip>
#include<ctime>
#include<string>
#define D(x) cout<<#x<<" = "<<x<<"  "
#define E cout<<endl
using namespace std;
typedef long long ll;
typedef pair<int,int>pii;
const int maxn=1e5*2+5;
const int INF=0x3f3f3f3f;
int t1[maxn],t2[maxn],sa[maxn],h[maxn],rk[maxn],c[10*maxn],a[maxn];
int m,n;
void calcsa(int n,int m){
    int *x=t1,*y=t2,p=0,f=0;
    for(int i=1;i<=m;i++)c[i]=0;
    for(int i=1;i<=n;i++)c[x[i]=a[i]]++;
    for(int i=1;i<=m;i++)c[i]+=c[i-1];
    for(int i=n;i;i--)sa[c[x[i]]--]=i;
    for(int i=1;i<=n&&p<=n;i<<=1){
        p=0;
        for(int j=n-i+1;j<=n;j++)y[++p]=j;
        for(int j=1;j<=n;j++)if(sa[j]>i)y[++p]=sa[j]-i;
        for(int j=1;j<=m;j++)c[j]=0;
        for(int j=1;j<=n;j++)c[x[y[j]]]++;
        for(int i=1;i<=m;i++)c[i]+=c[i-1];
        for(int j=n;j;j--)sa[c[x[y[j]]]--]=y[j];
        swap(x,y);x[sa[1]]=1;p=2;
        for(int j=2;j<=n;j++)
        x[sa[j]]=y[sa[j]]==y[sa[j-1]]&&y[sa[j]+i]==y[sa[j-1]+i]?p-1:p++;
        m=p;
    }
    for(int i=1;i<=n;i++)rk[sa[i]]=i;
    for(int i=1;i<=n;i++){
        int j=sa[rk[i]-1];
        if(f)f--;while(a[i+f]==a[j+f])f++;
        h[rk[i]]=f;
    }
}
int K;
char s1[maxn],s2[maxn];
int len1,len2;
ll ans;
struct node{
    int cnt,h;
    ll sum;
}st[maxn];
int top;
void init(){
    n=0;ans=0ll;
}
void solve(){
    top=0; //栈顶指针 
    for(int i=2;i<=n;i++){  
        if(h[i]<K) top=0; //按K分组 
        else{
            int cnt=0;
            while(top&&h[i]<=st[top].h) cnt+=st[top--].cnt; //维护单调递增性 记录后缀i前面比它的h值大的属于A的后缀个数 
            st[++top].cnt=cnt+(sa[i-1]<=len1);
            st[top].h=h[i];
            st[top].sum=top?st[top-1].sum:0;
            st[top].sum+=(ll)(h[i]-K+1)*st[top].cnt;
            if(sa[i]>=len1+1) ans+=st[top].sum;
        }
    }
    top=0;
    for(int i=2;i<=n;i++){
        if(h[i]<K) top=0;
        else{
            int cnt=0;
            while(top&&h[i]<=st[top].h) cnt+=st[top--].cnt; //维护单调递增性 记录后缀i前面比它的h值大的属于B的后缀个数 
            st[++top].cnt=cnt+(sa[i-1]>=len1+1);
            st[top].h=h[i];
            st[top].sum=top?st[top-1].sum:0;
            st[top].sum+=(ll)(h[i]-K+1)*st[top].cnt;
            if(sa[i]<=len1) ans+=st[top].sum;
        }
    }
}
int main() {
    ios::sync_with_stdio(false);
    //freopen("Common Substrings.in","r",stdin);
    while(scanf("%d",&K)&&K){
        scanf("%s%s",s1,s2);
        init(); 
        len1=strlen(s1),len2=strlen(s2);
        for(int i=0;i<len1;i++) a[++n]=s1[i]-' ';
        a[++n]='$'; //中间填入一个不可能出现的字符 
        for(int i=0;i<len2;i++) a[++n]=s2[i]-' ';
        calcsa(n,10000); //预处理sa和h数组 
        solve();
        printf("%lld\n",ans);
    }
    return 0;
}
#endif
#ifdef method_2
/*
sa模板 
*/
#include<bits/stdc++.h>
#define N 1000010
using namespace std;
int t1[N],t2[N],sa[N],h[N],rk[N],c[10*N],a[N];
char s[N];
int m,n;
void calcsa(int n,int m){
    int *x=t1,*y=t2,p=0,f=0;
    for(int i=1;i<=m;i++)c[i]=0;
    for(int i=1;i<=n;i++)c[x[i]=a[i]]++;
    for(int i=1;i<=m;i++)c[i]+=c[i-1];
    for(int i=n;i;i--)sa[c[x[i]]--]=i;
    for(int i=1;i<=n&&p<=n;i<<=1){
        p=0;
        for(int j=n-i+1;j<=n;j++)y[++p]=j;
        for(int j=1;j<=n;j++)if(sa[j]>i)y[++p]=sa[j]-i;
        for(int j=1;j<=m;j++)c[j]=0;
        for(int j=1;j<=n;j++)c[x[y[j]]]++;
        for(int i=1;i<=m;i++)c[i]+=c[i-1];
        for(int j=n;j;j--)sa[c[x[y[j]]]--]=y[j];
        swap(x,y);x[sa[1]]=1;p=2;
        for(int j=2;j<=n;j++)
        x[sa[j]]=y[sa[j]]==y[sa[j-1]]&&y[sa[j]+i]==y[sa[j-1]+i]?p-1:p++;
        m=p;
    }
    for(int i=1;i<=n;i++)rk[sa[i]]=i;
    for(int i=1;i<=n;i++){
        int j=sa[rk[i]-1];
        if(f)f--;while(a[i+f]==a[j+f])f++;
        h[rk[i]]=f;
    }
}
int main(){
    scanf("%s",s);int len=strlen(s);
    for(int i=0;i<len;i++)a[++n]=s[i]-' ';
    calcsa(n,10000);
    for(int i=1;i<=n;i++)printf("%d ",sa[i]);
    return 0;
}
#endif
#ifdef method_3

#endif

POJ 3729: Facer’s string
利用height将后缀分组,然后统计每一组内属于第一个串和第二个串分别有多少即可。
注意一定要保证a数组的数字大于等于1。
代码如下

/*

*/
#define method_1
#ifdef method_1
/*
利用height将后缀分组,然后统计每一组内属于第一个串和第二个串分别有多少即可。
注意一定要保证a数组的数字大于等于1。 
*/
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<vector>
#include<cstring>
#include<cstdlib>
#include<iomanip>
#include<ctime>
#include<string>
#define D(x) cout<<#x<<" = "<<x<<"  "
#define E cout<<endl
using namespace std;
typedef long long ll;
typedef pair<int,int>pii;
//const int maxn=50000*2+5;
const int maxn=500000*2+5;
const int maxnum=10000+5;
const int INF=0x3f3f3f3f;
int t1[maxn],t2[maxn],sa[maxn],h[maxn],rk[maxn],c[10*maxn],a[maxn];
int m,n;
void calcsa(int n,int m){
    int *x=t1,*y=t2,p=0,f=0;
    for(int i=1;i<=m;i++)c[i]=0;
    for(int i=1;i<=n;i++)c[x[i]=a[i]]++;
    for(int i=1;i<=m;i++)c[i]+=c[i-1];
    for(int i=n;i;i--)sa[c[x[i]]--]=i;
    for(int i=1;i<=n&&p<=n;i<<=1){
        p=0;
        for(int j=n-i+1;j<=n;j++)y[++p]=j;
        for(int j=1;j<=n;j++)if(sa[j]>i)y[++p]=sa[j]-i;
        for(int j=1;j<=m;j++)c[j]=0;
        for(int j=1;j<=n;j++)c[x[y[j]]]++;
        for(int i=1;i<=m;i++)c[i]+=c[i-1];
        for(int j=n;j;j--)sa[c[x[y[j]]]--]=y[j];
        swap(x,y);x[sa[1]]=1;p=2;
        for(int j=2;j<=n;j++)
        x[sa[j]]=y[sa[j]]==y[sa[j-1]]&&y[sa[j]+i]==y[sa[j-1]+i]?p-1:p++;
        m=p;
    }
    for(int i=1;i<=n;i++)rk[sa[i]]=i;
    for(int i=1;i<=n;i++){
        int j=sa[rk[i]-1];
        if(f)f--;while(a[i+f]==a[j+f])f++;
        h[rk[i]]=f;
    }
}
int len1,len2;
void init(){
    n=0;
}
//利用height将后缀分组,然后统计每一组内属于第一个串和第二个串分别有多少。
int solve(int k){
    int res=0;
    for(int i=1;i<=n;i++){
        if(h[i]<k) continue; 
        //h[i]>=k  排名为i的后缀与排名为i-1的后缀的最长公共前缀长度>=k 
        int one=0,two=0;
        if(sa[i-1]<=len1) one++;
        if(sa[i-1]>=len1+1) two++;
        for(;i<n,h[i]>=k;i++){ //统计每一组内属于第一个串和第二个串分别有多少
            if(sa[i]<=len1) one++;
            if(sa[i]>=len1+1) two++;
        }
        if(two) res+=one; //只要匹配上了 就有贡献 
    }
    return res;
}
int main() {
//  ios::sync_with_stdio(false);
    //freopen("Facer’s string.in","r",stdin);
    int k;
    while(scanf("%d%d%d",&len1,&len2,&k)!=EOF){
        init();
        int temp;
        for(int i=1;i<=len1;i++) scanf("%d",&temp),a[++n]=temp+1;//注意一定要保证a数组的数字大于等于1
        a[++n]=maxnum;
        for(int i=1;i<=len2;i++) scanf("%d",&temp),a[++n]=temp+1;
//      for(int i=1;i<=n;i++) D(a[i]);
//      E;
        calcsa(n,maxnum);
//      for(int i=1;i<=n;i++) D(h[i]); //h[i]为排名为i的后缀与排名为i-1的后缀的最长公共前缀 
//      E;
        int ans=solve(k)-solve(k+1); //lcp大于等于k的子串数-lcp大于等于k+1的子串数 
        printf("%d\n",ans);
    }
    return 0;
}
#endif
#ifdef method_2
/*

*/

#endif
#ifdef method_3
/*

*/

#endif

相关文章

  • Javascript知识点整合

    字符串 单行字符串: ‘字符串’或“字符串” 多行字符串: `多行字符串` 字符串操作: 字符串连接‘+’号 长度...

  • C++基础字符串

    字符串的构造 字符串特性描述 字符操作 字符串赋值 字符串连接 字符串比较 字符串查找 字符串替换 字符串删除 字...

  • iOS中的NSString与NSMutableString

    字符串的创建 字符串读写 字符串的比较 字符串的搜索 字符串截取 字符串替换 字符串与路径 字符串转换 NSMut...

  • iOS NSString用法总结

    字符串属性 字符串截取 字符串比较 字符串搜索 字符串拼接 字符串基本类型转换 字符串分行,分段 字符串列举(按条...

  • php 字符串常见方法汇总

    字符串拼接 字符串检索 字符串截取 字符串替换 字符串大小写转化 字符串转数组 字符串格式化

  • iOS 字符串截取、iOS 字符串替换、iOS 字符串分隔、iO

    iOS之字符串截取、iOS 字符串替换、iOS字符串分隔、iOS之字符串匹配、截取字符串、匹配字符串、分隔字符串 ...

  • PHP中字符串函数库常用函数解析 -- PHP 学习 (十一)

    常用字符串函数分类: 字符串长度, 字符串查找, 字符串大小写转换, 字符串截取, 字符串 ASCII, 字符串加...

  • Kotlin语言(二):字符串类型

    1、字符串定义 2、字符串删除空格 3、字符串比较 4、字符串切割 5、字符串截取 6、字符串替换 7、字符串模板

  • 字符串扩展

    求字符串大小 字符串解码、转换 字符串截取 字符串汉字处理 字符串 Mac地址 字符串进制转换

  • 2020-09-30字符串

    day8-字符串 字符串的操作 in 和 not in字符串1 in 字符串2 - 判断字符串1是否是字符串...

网友评论

    本文标题:字符串

    本文链接:https://www.haomeiwen.com/subject/gigguctx.html