2018 Spring Training 字符串合集

非常非常长…一堆破题目还是折腾了挺久的…本来我就弱…字符串部分更加是弱QAQ…做起来非常不顺利qwq ==
A.HDU3374
题目其实很简单,就是求给定串的最大最小表示法,然后求出现的次数的话,就把原串复制一遍接在串尾,然后跑一遍kmp求出现次数即可。

#include<cstdio>
#include<iostream>
#include<string>
#include<algorithm>
#include<cstring>
using namespace std;
int minpos(string str,int len)
{
    int i=0,j=1,k=0,t;
    while(i<len&&j<len&&k<len){
        t=str[(i+k)>=len?i+k-len:i+k]-str[(j+k)>=len?j+k-len:j+k];
        if(!t)k++;
        else{
            if(t>0)i=i+k+1;
            else j=j+k+1;
            if(i==j){
                ++j;
            }
            k=0;
        }
    }
    return min(i,j);
}
int maxpos(string str,int len)
{
    int i=0,j=1,k=0,t;
    while(i<len&&j<len&&k<len){
        t=str[(i+k)>=len?i+k-len:i+k]-str[(j+k)>=len?j+k-len:j+k];
        if(!t)k++;
        else{
            if(t<0)i=i+k+1;
            else j=j+k+1;
            if(i==j){
                ++j;
            }
            k=0;
        }
    }
    return min(i,j);
}
int next1[1000005];
void getnext(string str)
{
    next1[0]=-1;
    int k=-1,j=0;
    while(j<str.size()-1){
        if(k==-1||str[k]==str[j]){
            k++;j++;
            next1[j]=k;
        }
        else{
            k=next1[k];
        }
    }
}
int main()
{
    string str;int i,j,len;
    while(cin>>str){
        int len=str.size();
        int pos1=minpos(str,len),pos2=maxpos(str,len);
        str+=str;string str2=str.substr(pos1,len),str3=str.substr(pos2,len);str=str.substr(0,str.size()-1);//一定要减掉最末位,否则会出错
        memset(next1,0,sizeof(next1));getnext(str2);
        i=0,j=0;int cnt1=0,cnt2=0;
        while(i<str.size()){//因为是输出s1中所有s2的位置所以while里只对s1的长度做出限制
            if(j==-1||str[i]==str2[j]){
                if(j==str2.size()-1){//判断一定要放在这里,否则i++,j++会导致重叠部分无法利用,比如ABABABAB和ABAB的匹配
                    cnt1++;j=next1[j];continue;
                }
                i++;j++;
            }
            else{
                while(j>=0&&str2[j]!=str[i])//这里注意要加等于号
                    j=next1[j];
            }
        }
        memset(next1,0,sizeof(next1));getnext(str3);
        i=0,j=0;
        while(i<str.size()){
            if(j==-1||str[i]==str3[j]){
                if(j==str3.size()-1){
                    cnt2++;j=next1[j];continue;
                }
                i++;j++;
            }
            else{
                while(j>=0&&str3[j]!=str[i])//这里注意要加等于号
                    j=next1[j];
            }
        }
        cout<<pos1+1<<' '<<cnt1<<' '<<pos2+1<<' '<<cnt2<<endl;
    }

    return 0;
}

B.HDU1880
其实就是求一堆字符串的对应关系,很容易就会想到偷懒用map搞一搞,然而会mle…==
就算改成map里一个int一个string依然会mle==
于是只好把所有输入的字符拿string数组存起来,然后计算他们的hash值,把hash值映射为string数组的下标…然而由于hash过于玄学…因此wa了n次…说好的综合性能最好的hash呢…只好换了一个

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<string>
#include <map>
using namespace std;
unsigned int ELFHash(string str)
{
//    unsigned int hash = 0;
//    unsigned int x    = 0;
//
//    for(int i=0;i<str.size();i++)
//    {
//        hash = (hash << 4) + (str[i]);
//        if ((x = hash & 0xF0000000L) != 0)
//        {
//            hash ^= (x >> 24);
//            hash &= ~x;
//        }
//    }
//
//    return (hash & 0x7FFFFFFF);
    unsigned int seed = 131;
    unsigned int h = 0;
    for (int i=0;i<str.size();i++) {
        h = h * seed + (str[i]);
    }
    return (h & 0x7FFFFFFF);
}
int main()
{
    string str,str1[100005],str2[100005];
    //unsigned int hash1[100005],hash2[100005];
    map<int,int>mp1,mp2;int i=0;
    while(cin>>str1[++i]&&str1[i]!="@END@"){
        getchar();getline(cin,str2[i]);
        unsigned int hash1=ELFHash(str1[i]),hash2=ELFHash(str2[i]);
        mp1[hash1]=i;mp2[hash2]=i;
    }
    int n;cin>>n;getchar();
    while(n--){
        getline(cin,str);
        if(str[0]=='['){
            unsigned int hash1=ELFHash(str);
            int pos=mp1[hash1];
            if(pos==0){
                puts("what?");
            }
            else{
                cout<<str2[pos]<<endl;
            }
        }
        else{
            unsigned int hash1=ELFHash(str);
            int pos=mp2[hash1];
            if(pos==0){
                puts("what?");
            }
            else{
                str=str1[pos].substr(1,str1[pos].size()-2);
                cout<<str<<endl;
            }
        }
    }

    return 0;
}

C.HDU1251
裸的trie,把tag改成cnt然后插入单词的时候累加一下就行,查询的时候输出对应点的cnt即可。

#include<cstdio>
#include<iostream>
#include<vector>
#include<cstring>
#include<algorithm>
#include<string>
using namespace std;
struct node{
    char c;
    vector<node*>s;//其实也可以开一个26个节点的数组,那样比较节省查找时间,但是浪费空间
    bool tag,ask;//因为到达某个节点后即使完全匹配,也可能只是另一个前缀相同的单词的一部分,
    // 所以需要一个标记来标记这个节点是否是某单词的结尾
    int cnt;
    node(){//新建函数
        tag=false;ask=false;s.clear();cnt=0;
    }
}*root=new node();
void add(string str)
{
    int i,j;
    struct node*p=root;
    for(i=0;i<str.size();i++){
        for(j=0;j<p->s.size();j++){
            if(p->s[j]->c==str[i])break;
        }
        if(j==p->s.size()){//如果找不到
            node*t=new node();t->c=str[i];
            p->s.push_back(t);p=t;
        }
        else
            p=p->s[j];
        p->cnt++;
    }
    p->tag=true;//单词结尾所在节点标记
}
int query(string str)
{
    int i,j;node*p=root;
    for(i=0;i<str.size();i++){
        for(j=0;j<p->s.size();j++){
            if(p->s[j]->c==str[i])break;
        }
        if(j==p->s.size())return 0;
        p=p->s[j];
    }

    return p->cnt;
}
int main()
{
    string str;
    while(getline(cin,str)&&str.end()!=str.begin()){
        add(str);
    }
    while(cin>>str){
        cout<<query(str)<<endl;
    }

    return 0;
}

D.HihoCoder - 1032
Manacher模板,实在没啥好说的。

#include<cstdio>
#include<iostream>
#include<string>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn=1100000;
char str1[maxn];//原字符串
char tmp1[maxn<<1];//转换后的字符串
int Len1[maxn<<1];
//转换原始串
int INIT(char *st)
{
    int i,len=strlen(st);
    tmp1[0]='@';//字符串开头增加一个特殊字符,防止越界
    for(i=1;i<=2*len;i+=2)
    {
        tmp1[i]='#';
        tmp1[i+1]=st[i/2];
    }
    tmp1[2*len+1]='#';
    tmp1[2*len+2]='$';//字符串结尾加一个字符,防止越界
    tmp1[2*len+3]=0;
    return 2*len+1;//返回转换字符串的长度
}
//Manacher算法计算过程
int MANACHER(char *st,int len)
{
    int mx=0,ans=0,po=0;//mx即为当前计算回文串最右边字符的最大值
    for(int i=1;i<=len;i++)
    {
        if(mx>i)
            Len1[i]=min(mx-i,Len1[2*po-i]);//在Len[j]和mx-i中取个小
        else
            Len1[i]=1;//如果i>=mx,要从头开始匹配
        while(st[i-Len1[i]]==st[i+Len1[i]])
            Len1[i]++;
        if(Len1[i]+i>mx)//若新计算的回文串右端点位置大于mx,要更新po和mx的值
        {
            mx=Len1[i]+i;
            po=i;
        }
        ans=max(ans,Len1[i]);
    }
    return ans-1;//返回Len[i]中的最大值-1即为原串的最长回文子串额长度
}
int main()
{
    int n;cin>>n;
    while(n--) {
        cin >> str1;memset(Len1,0,sizeof(Len1));
        int len = INIT(str1);
        cout << MANACHER(tmp1, len) << endl;
    }

    return 0;
}

E.HDU3746
用kmp的next数组求最小循环节即可。

#include<cstdio>
#include<iostream>
#include<cstring>
//#include<string>
using namespace std;
int next1[100005];
char str[100005];
void getnext(void)
{
    next1[0]=-1;
    int k=-1,j=0;
    int len=strlen(str);
    while(j<len){
        if(k==-1||str[k]==str[j]){
            k++;j++;
            next1[j]=k;
        }
        else{
            k=next1[k];
        }
    }
}
int main()
{
    int t,i,j;
    cin>>t;getchar();
    while(t--){
        scanf("%s",str);int len1=strlen(str);
        getnext();
        //for(i=1;i<=str.size();i++)cout<<next1[i]<<endl;
        int len=len1-next1[len1];
        int ans=(len-len1%len);
        if(len1>len)ans%=len;
        cout<<ans<<endl;
    }

    return 0;
}

F.HDU6153
题意
给出两个字符串 A 和 B ,求 B 的所有后缀在 A 中的出现次数与后缀长度的乘积和。
思路
枚举所有后缀显然不是理想的方案,但是我们把两个串倒过来,问题就变成了求 B 的所有前缀在 A 中出现次数与前缀长度的乘积和。
于是便是 扩展 KMP 的基础题目啦。
对于 extend[i] 所保存的 y[i..n-1] 与 x[0..m-1] 的最长公共前缀。
我们可以枚举所有的 i ,对于每一个 i ,结果便是 1+2+3+…+extend[i] ,即 extend[i]×(extend[i]+1)2 。
累计求和即可。
这里写图片描述

#include<iostream>
#include<string>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
/* 求解T中next[],注释参考GetExtend() */
const int mod=1e9+7;
void GetNext(string T, int next1[])
{
    int t_len = T.size();
    next1[0] = t_len;
    int a;
    int p;

    for (int i = 1, j = -1; i < t_len; i++, j--)
    {
        if (j < 0 || i + next1[i - a] >= p)
        {
            if (j < 0)
                p = i, j = 0;

            while (p < t_len&&T[p] == T[j])
                p++, j++;

            next1[i] = j;
            a = i;
        }
        else
            next1[i] = next1[i - a];
    }
}
//题定义:给定两个字符串S和T(长度分别为n和m),下标从0开始,定义extend[i]等于S[i]…S[n-1]与T的最长公共前缀的长度,求出所有的extend[i]。
/* 求解extend[] */
void GetExtend(string S, string T, int extend[], int next[])
{
    GetNext(T, next);  //得到next
    int a;
    int p;             //记录匹配成功的字符的最远位置p,及起始位置a
    int s_len = S.size();
    int t_len = T.size();

    for (int i = 0, j = -1; i < s_len; i++, j--)  //j即等于p与i的距离,其作用是判断i是否大于p(如果j<0,则i大于p)
    {
        if (j < 0 || i + next[i - a] >= p)  //i大于p(其实j最小只可以到-1,j<0的写法方便读者理解程序),
        {                                   //或者可以继续比较(之所以使用大于等于而不用等于也是为了方便读者理解程序)
            if (j < 0)
                p = i, j = 0;  //如果i大于p

            while (p < s_len&&j < t_len&&S[p] == T[j])
                p++, j++;

            extend[i] = j;
            a = i;
        }
        else
            extend[i] = next[i - a];
    }
}

int main()
{
    int t,i,j;cin>>t;getchar();
    while(t--) {
        string s1,s2;
        int next1[1000005] = {0};
        int extend[1000005] = {0};
        getline(cin,s1);getline(cin,s2);
        reverse(s1.begin(),s1.end());reverse(s2.begin(),s2.end());
        //GetNext(s1,next1);
        GetExtend(s1,s2,extend,next1);
        long long ans=0;
        for(i=0;i<s1.size();i++){
            ans+=(long long)(extend[i]+1)*(long long)extend[i]/2;ans%=mod;
        }
        cout<<ans<<endl;
    }

    return 0;
}

G.CodeForces - 4C
水题,拿map查重顺便统计一下该加哪个后标就行

#include<cstdio>
#include<iostream>
#include<set>
#include<algorithm>
#include<cstring>
#include<string>
#include <unordered_set>
#include<map>
using namespace std;
int main()
{
    int n,i,j;
    cin>>n;string str;
    map<string,int>mp;
    while(n--){
        cin>>str;i=mp[str];
        if(!i){
            puts("OK");mp[str]++;
        }
        else{
            mp[str]++;string str1;
            while(i){
                str1+='0'+i%10;i/=10;
            }
            reverse(str1.begin(),str1.end());cout<<str+str1<<endl;
        }
    }

    return 0;
}

H.CodeForces - 471D
相当有意思的一个题。我们会发现,其实要想在长的墙中看到目标墙,其实就是希望在长的墙中有连续的一段,其中相邻两项的差与目标墙相同。那么我们在读入高度的时候只保存相邻项的差即可。然后注意到其实墙的起点可以随意弄到想要的高度,因此我们kmp匹配的时候从起点后面的一个点开始匹配即可。
注意n或者w等于1的时候要特判。

#include<cstdio>
#include<iostream>
#include<cstring>
#include<string>
#include<vector>
using namespace std;
int next1[200005];
void getnext(vector<int>str)
{
    next1[0]=-1;
    int k=-1,j=0;
    while(j<str.size()-1){
        if(k==-1||str[k]==str[j]){
            k++;j++;
            next1[j]=k;
        }
        else{
            k=next1[k];
        }
    }
    return;
}
int main() {
    int n, w, i, j, k;
    vector<int> str1, str2;
    //cin>>str1>>str2;
    cin >> n >> w;
    int num[200005];cin>>num[0];
    for(i=1;i<n;i++){
        scanf("%d",&num[i]);str1.push_back(num[i]-num[i-1]);
    }cin>>num[0];
    for(i=1;i<w;i++){
        scanf("%d",&num[i]);str2.push_back(num[i]-num[i-1]);
    }
    if (n == 1) {
        if (w <= 1) {
                cout<<1<<endl;
        }
        else{
            cout<<0<<endl;
        }
        return 0;
    }
    if(w==1){
        cout<<n<<endl;return 0;
    }
    getnext(str2);
    i = 0, j = 0;int ans=0;
    while (i < str1.size()) {
        if (j == -1 ||  str1[i] == str2[j]) {
            if (j == str2.size() - 1) {//判断一定要放在这里,否则i++,j++会导致重叠部分无法利用,比如ABABABAB和ABAB的匹配
                //cout << i - str2.size() + 1 << endl;
                ans++;j = next1[j];
                continue;
            }
            i++;j++;
        }
//        else if(j==0){
//            if(str2[1]-str2[0]==str)
//        }
        else {
            while (j >= 0 && str2[j] != str1[i])//这里注意要加等于号
                j = next1[j];
        }
    }
    cout<<ans<<endl;

    return 0;
}

I.HDU4333
题意:给出一个不含前导0的数字,每一次把它的最后一位移动到最前面,一直持续下去,分别求形成的数字小于,等于和大于原来数的个数。
例如:134可以形成134,341,413三个数,所以分别是1,1,1。
如果移动过程中出现前导0,则去掉前导0,并认为形成了新的数字。

扩展KMP能求出一个串所有后缀串(即s[i…len])和模式串的最长公共前缀。于是只要将这个串复制一遍,求出该串每个后缀与其本身的最长公共前缀即可,当公共前缀>=len时,显然相等,否则只要比较下一位就能确定这个串与原串的大小关系。

至于重复串的问题,只有当这个串有循环节的时候才会产生重复串,用KMP的next数组求出最小循环节,用长度除以最小循环节得到循环节个数,在将3个答案都除以循环节个数即可。
是否有循环节的判断方法:
这里写图片描述

#include<iostream>
#include<string>
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=1e5+5;
void GetNext(string T, int next[])
{
    int t_len = T.size();
    next[0] = t_len;
    int a;
    int p;

    for (int i = 1, j = -1; i < t_len; i++, j--)
    {
        if (j < 0 || i + next[i - a] >= p)
        {
            if (j < 0)
                p = i, j = 0;

            while (p < t_len&&T[p] == T[j])
                p++, j++;

            next[i] = j;
            a = i;
        }
        else
            next[i] = next[i - a];
    }
}
int next1[N],next2[N*2];
void getnext(string str)
{
    next1[0]=-1;
    int k=-1,j=0;
    while(j<str.size()){
        if(k==-1||str[k]==str[j]){
            k++;j++;
            next1[j]=k;
        }
        else{
            k=next1[k];
        }
    }
}
int main() {
    int n, i, j;
    cin >> n;int cnt=0;
    getchar();
    while (n--) {
        cnt++;
        string str1, str2;
        getline(cin, str1);
        int cntl=0,cnte=0,cntg=0;
        str2 = str1 + str1;
        getnext(str1);
        GetNext(str2, next2);
        int xh=0;
        if(next1[str1.size()]&&str1.size()%(str1.size()-next1[str1.size()])==0) {
            xh = str1.size() - next1[str1.size()];
            xh = str1.size() / xh;
        }
        for(i=0;i<str1.size();i++){
            if(next2[i]>=str1.size())cnte++;
            else{
                int len=next2[i];
                if(str2[len]>str2[len+i])
                    cntl++;
                else cntg++;
            }
        }
        if(xh){
            cntl/=xh;cnte/=xh;cntg/=xh;
        }
        printf("Case %d:",cnt);
        cout<<" "<<cntl<<' '<<cnte<<' '<<cntg<<endl;
    }

    return 0;
}

J.CodeForces - 608E
太难了,本蒟蒻不会了QAQ ==

L.CodeForces - 282E
题意: 题中给出了包含 n 个数的一串数列a1, a2, …, an,规定前缀和 pre[ i ] 为前 i 个数的异或和,后缀和 suf[ i ] 为后 i 个数的异或和 (即 pre[ i ] =a1 xor a2 xor a3…….xor ai , suf[ i ]同理),现求一个前缀和 pre[ x ] 和后缀和 suf[ y ] 使得 pre [ x ] xor suf[ y ]最大,要求 x 小于 y ,即所选的前缀和后缀不能交叉
,前缀和后缀可以为空,此时值为0。
嘛…描述的过于繁琐了….
注意题上给的数据范围!

首先我们能想到裸的n^2做法,但显然是不行的,鉴于刚刚做了一个和这类似的题且正在做字典树专题,立马想到了字典树来做……….

既然超时是因为枚举两遍,那么我们可不可以只枚举一遍呢?

想到两个数间异或的运算是两个数转成二进制后的每一位相互比较,相同为0,不同为1,既然要求最长路径,那我们就可以每次贪心的每一位都去取和自己不同数,如果找不到那就只好取相同的数,(即假如 x 的第 i 位是 0 那么他最优选择就是第 i 位为 1 的数,如果找不到第 i 位为1的就只能找第 i 位为 0 的)优先考虑最高位,再到最低位,因为二进制数 1000 是肯定大于 0XXX 的,这样的话就问题就转到了怎么在一堆序列中找到一个特定的序列,就想到了用字典树来解决问题,本题的数据比较坑,超了 int 的范围,因此要用 long long 因为最大有10^12,大概是2的四十次方左右,但因为这只是单个数的值加以来后可能会更大,所以就选择2的50次方,那么就开一个有50层的字典树,每层有0和1两个儿子,字典树从上往下存高位到低位。
将后缀从前往后扫,每次相当于后缀和的取值减少了最前面的一位,而加入之前的前缀和异或上当前这一位并把这个值插入trie,意味着前缀和多了一种选择,那么在这种情况下找出当前状况下的最优解并尝试去更新答案即可。

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<string>
using namespace std;
int cnt=0;
typedef long long ll;
struct node {
    int son[2];

    void init() {
        son[0] = son[1] = -1;
    }
}ch[10000005];
void insert(ll x) {
    for (int i = 63, point = 0; ~i; i--) {
        int cur;
        if ((1LL << i) & x)cur = 1;
        else cur = 0;
        if (ch[point].son[cur] == -1) {
            ch[++cnt].init();
            ch[point].son[cur] = cnt;
        }
        point = ch[point].son[cur];
    }
}
ll find(ll x) {
    ll res = 0;
    for (int i = 63, point = 0; ~i; i--) {
        int cur = ((1LL << i) & x) == 0;//注意1LL!
        if (ch[point].son[cur] == -1)cur ^= 1;//如果这个当前位最优解不存在,只能取不优的解
        res = (res << 1) + cur;
        point = ch[point].son[cur];
    }
    return res;
}
ll num[100005],pre1=0,suf=0;
int main()
{
    int n,i,j,k;
    cin>>n;
    for(i=1;i<=n;i++){
        scanf("%lld",&num[i]);suf^=num[i];
    }
    ll ans=suf;ch[0].init();
    for(i=0;i<=n;i++){
        suf^=num[i];pre1^=num[i];//前缀和后缀和改变
        insert(pre1);
        ans=max(ans,suf^find(suf));
    }
    cout<<ans<<endl;

    return 0;
}

M.CodeForces - 514C
题目做法与最后一题很类似,就是把原来的那些目标字符串先hash一波存进set里,然后询问的字符串读入后枚举每一位进行替换,看看替换后的hash值是否是set里存在的。注意替换后的字符串必须是与原串恰好有一位不同,也就是替换的字母一定要与原字母不同即可。然后hash的时候种子选131会冲突…选257就能过…玄学orz

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<set>
using namespace std;
typedef long long ll;
const ll M=1000000007;
const int N=650000;
int main()
{
    int n,m,i,j,k,t;ll seed[650000];
    for(seed[0]=i=1;i<N;i++) seed[i]=seed[i-1]*257%M;
    char str[650000];
    set<ll>s;
    cin>>n>>m;
    for(i=1;i<=n;i++) {
        scanf("%s",str);ll res=0;int len=strlen(str);
        for(j=0;j<len;j++) res=(res*257+str[j])%M;
        s.insert(res);
    }
    for(i=1;i<=m;i++){
        scanf("%s",str);bool isok=false;
        int len=strlen(str);ll res=0;
        for(j=0;j<len;j++) res=(res*257+str[j])%M;
        for(j=0;j<len;j++){
            for(char c='a';c<='c';c++){
                if(str[j]==c)continue;//注意替换后必须与原来位上的字母不同!!!
                if(s.find((((c-str[j])*seed[len-j-1]+res)%M+M)%M)!=s.end()){
                     puts("YES");isok=true;break;
                 }
            }
            if(isok)break;
        }
        if(!isok)puts("NO");
    }

    return 0;
}

N.HYSBZ - 3670
NOI2014的原题…然而蒟蒻如我搞了很久才大概明白,先挖个坑。
这里写图片描述
这里写图片描述

#include <cstdio>
#include <cstring>
#include<iostream>
using namespace std;
const int MAXT = 5;
const int MAXN = 1000000;
const unsigned long long MOD = 1000000007;
int n, next1[MAXN + 1], num[MAXN + 1], num2[MAXN + 1];
char s[MAXN + 1];
int main() {
    int t = 1;
    scanf("%d", &t);
    while (t--) {
        scanf("%s", s);
        n = strlen(s);
        next1[0] = next1[1] = num[0] = num[1] = 0;
        for (int i = 2, t = 0, k = 0; i <= n; i++) {
            while (t && s[t] != s[i - 1]) t = next1[t];
            while ((k && s[k] != s[i - 1]) || k >= i / 2) k = next1[k];

            if (s[k] == s[i - 1]) num2[i] = num[++k] + 1;
            else num2[i] = 0;

            if (s[t] == s[i - 1]) next1[i] = ++t, num[i] = num[t] + 1;
            else next1[i] = num[i] = 0;
        }
        unsigned long long ans = 1;
        for (int i = 1; i <= n; i++) (ans *= (num2[i] + 1)) %= MOD;
        printf("%llu\n", ans);
    }

    return 0;
}

O.HYSBZ - 3555
枚举每一个字符串,枚举该串上的每一位,计算该串减去这一位后的hash值并把它存起来。然后把所有存起来的hash值排一波序,看每个hash值相同的区间可以有多少对相似的字符串即可。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
unsigned long long hash1[6000010],seed[210],t;
char str[310];
int main()
{

    int n,m,s,ans,cnt;
    scanf("%d%d%d",&n,&m,&s);
    int i,j;
    for(seed[0]=i=1;i<m;i++) seed[i]=seed[i-1]*131;
    for(i=1;i<=n;i++) {
        scanf("%s", str);
        for (t = j = 0; j < m; j++) t = t * 131 + str[j];
        for (j = 0; j < m; j++) hash1[++cnt] = t - seed[m - j - 1] * str[j];
    }
    sort(hash1+1,hash1+cnt+1);
    int segment=1;
    for(i=2;i<=cnt;i++) {
        if (hash1[i] == hash1[i - 1]) segment++;
        else ans += segment * (segment - 1) / 2, segment = 1;
    }
    ans+=segment*(segment-1)/2;
    cout<<ans<<endl;

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值