2018 Spring Training 字符串合集

YOLOv8【第十六章:可视化、论文复现与写作篇·第15节】Introduction 写作逻辑:如何讲好“Motivation”故事! 🏆 本文收录于 《YOLOv8实战:从入门到深度优化》 专栏。该专栏系统复现并梳理全网各类 YOLOv8 改进与实战案例(当前已覆盖分类 / 检测 / 分割 / 追踪 / 关键点 / OBB 检测等方向),坚持持续更新 + 深度解析,质量分长期稳定在 97 分以上,可视为当前市面上 覆盖较全、更新较快、实战导向极强 的 YOLO 改进系列内容之一。 阅读详情

非常非常长…一堆破题目还是折腾了挺久的…本来我就弱…字符串部分更加是弱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;
}
【信息科学与工程学】【数据中心】第三十五篇 云计算数据中心的学科知识04 编号学科(课程)核心知识点在云计算/云存储/云网络/云安全/云MaaS中的作用代表教材/资料/论文 + 数学方程式列表工业界应用D1421​云原生数据库:TiDB​HTAP(混合事务/分析处理)、分布式SQL、水平扩展、强一致(Raft)、自动故障恢复、与MySQL兼容、TiFlash列式引擎提供弹性扩展的分布式数据库;支撑高并发在线交易与实时分析教材:《TiDB in Action》PingCAP(2020) 论文:《TiDB: A Raft-based HTAP Database》(2017) 阅读详情

相关推荐

springtraining

Spring培训 它包含有关 Spring 的简单示例。 弹簧模块 ![弹簧模块] ( ) 弹簧生命周期 ![Spring 生命周期] ( ) 有用的链接: Spring 中使用的基本注解(org.springframework.sterotype) @控制器 @Repository @成分 @服务 数据源配置方法:(各有特点) 使用 JDBC 驱动程序(不推荐用于大型项目) 使用 JNDI 使用DBCP , Spring平台独立事务管理器 Java 世界最著名的 ORM 框架 Hibernate Eclipse链接 开放式JPA 代理模式 Spring MVC - 三元架构演示 Spring 可以集成到现有的 web 框架中,但也只支持开发基于 spring 的 web 应用程序。 Spring 使 Web 编程成为可能的一些领域 SpringMVC

Spring Training I C

** C. Inna and Huge Candy Matrix** 题意: 给你一个矩阵询问该矩阵经过某点经过顺时针旋转X次,左右翻转Y次,逆时针旋转Z次,问最终该点的坐标 思路: 此题只需推出几个旋转后坐标的公式就行,但需要注意的是,旋转后有可能行列数会发生变化,行边为列,列变为行,而且记得每次初始化,wa了无数发以上两点 #include&lt;bits/stdc++.h&gt; #de...

mzip的博客 265

SpringTraining

使用 Spring 和 Hibernate 进行库管理: #封装了事务管理

获取两日期之间的所有日期,并格式化为字符串集合

private List&lt;Date&gt; getDates(Date startDate, Date endDate) throws ParseException{ List&lt;Date&gt; result = new ArrayList&lt;Date&gt;(); if (startDate.getTime() == endDate.getTime()) { result...

SongJingzhou的博客 406

Ubuntu虚拟机磁盘扩容实战:5分钟搞定gparted分区调整(附常见错误修复)

本文详细介绍了在Ubuntu虚拟机中使用gparted工具进行磁盘扩容的实战步骤。针对常见的交换分区阻碍问题,提供了从禁用交换空间、删除旧分区、调整主分区大小到重建交换分区并更新系统配置的完整解决方案,帮助用户在5-10分钟内安全高效地解决磁盘空间不足的难题。

1a2s3d4f5g的博客 669

解密CANoe数据回放:从BLF到ASC的格式转换与优化策略

本文深入解析CANoe数据回放中BLF与ASC格式的转换与优化策略,帮助工程师高效处理汽车电子测试数据。详细介绍了两种格式的特性、转换方法及性能优化技巧,包括CANoe内置功能、命令行自动化和第三方工具应用,提升故障诊断和系统验证效率。

weixin_28331555的博客 535

Training: ASCII (Training, Encoding) 的解决

题目 题目很简单,就是将下面的ascii码转成字符。 84, 104, 101, 32, 115, 111, 108, 117, 116, 105, 111, 110, 32, 105, 115, 58, 32, 108, 112, 102, 110, 111, 115, 114, 104, 99, 98, 114, 108 实现 在Python中,有个函数chr()可以将数字转成ascii字符。 代码: raw = [84, 104, 101, 32, 115, 111, 108, 117,

hello world 467

水电站数字孪生:水力发电在可视化领域的应用

为智慧水坝领域提供了 B/S 架构的基于 WebGL 的图形引擎,与 GIS、粒子仿真、虚拟现实、边缘计算等技术相结合,融合多元业务系统数据,形成数字孪生界面,助力水利水电工程进一步提升数字化水平,乃至为整个工业领域添砖加瓦。

二三维数据可视化/数字孪生 2255

NJUPT2018 spring training 1--字符串

FZU2275 http://blog.youkuaiyun.com/mr_treeeee/article/details/76222301 A,B分别有有一个数,如果A的数小于B的数,那么一定是B赢。 否则,判断B中有没有A的子字符串或者B的逆序有没有A的子串。kmp算法。如果b为0或者B的长度为1,那么A一定会赢。 注意:这里0&lt;A,B&lt;10^1000000,要用字符串存储数据...

xx_ii 300

MMORPG网络游戏开发之任务系统的架构与设计

在网路游戏中做任务已经成为游戏很重要的一个核心功能和玩法,如何做好一个灵活可扩展的任务系统的架构与设计,今天来给大家分享一些我们的设计经验。接下来我把整个的任务系统分成以下6个模块:对于单机游戏而言,这6个模块都放在客户端直接处理,对于网路游戏而言,模块1~5实现在服务端,模块6实现在客户端。对啦!这里有个游戏开发交流小组里面聚集了一帮热爱学习unity的零基础小白,也有一些正在从事unity开发的技术大佬,欢迎你来交流学习。...

voidinit的专栏 3049

实测有效防止colab自动断开连接

实测有效防止colab自动断开连接

Thebest_jack的博客 1万+

BUPT Spring Training 6 4.06 总结

挂的是 ICPC Asia Jakarta Regional Site 2012 上来我先读A,发现是道水题,19''  1Y,速度太慢了。 然后她们看C题水,但是漏了个情况, 44''  2Y。 然后做G题,第一个想法太天真,还好敲着敲着发现不能过第四个样例,然后又花了一些时间,终于捣鼓出来了,但是这一路磕磕碰碰地花了太多时间。2'15'

Tri_integral的专栏 1050

VsCode+JLink+GCC替代Keil开发GD32程序-个人总结

对个人使用vscode+Jlink开发GD32程序做了总结,供需要者参考。

MyCollege1999的博客 2762

Unity企业内训(第1讲:Unity中C#开发基础)(Yanlz+Unity+Training+C#+数组+枚举+结构体+类+面向对象+抽象类+静态类+接口+泛型+集合+委托+设计模式+立钻哥哥+)

《Unity企业内训》 版本 作者 参与者 完成日期 备注 Unity_Training_V01_1.0 严立钻   2018.09.11     ...

VRunSoftYanlz的博客 3703

永磁同步电机MTPA控制详解:从理论到实践的全方位指南

永磁同步电机(PMSM)凭借其高功率密度卓越的效率以及精准的控制性能,已在工业驱动、电动汽车和家用电器等领域获得广泛应用。而在PMSM的诸多控制策略中,最大转矩电流比(MTPA)控制因其能显著提升电机效率而占据核心地位。在输出特定转矩时,通过优化直轴电流(Id)和交轴电流(Iq)的分配,使定子电流的幅值最小化。对于电机测试工程师而言,理解MTPA意味着掌握了一把提升系统性能的关键钥匙。为什么这一点如此重要?因为定子电流的减小直接带来了铜损的降低。

优快云-新能源汽车研发测试专业博主 516

HyperLynx中层叠设计实例

这两种方式有点区别,前一种方式只是增加层,需要自行修改层的类型,后一种方式可以选定为某一种类型,如果是增加信号层,软件会默认同时增加一层介质层。层叠已经有8层,按照示例的层叠有4个信号层、3个地平面层和1个电源平面层,对照进行层名称(LayerName)、介质类型(Type)及用途(Usage)设置。在这列中,有的是白色,有的是淡蓝色。白色的说明可以修改,如第一行是介质,通常在最外层的介质有两个用途,阻焊层和介质,单击该行会出现一个下拉菜单,选择SolderMask,即为阻焊层。...

小幽余生不加糖 5874

BERT:用于语言理解的深度双向 Transformer 预训练

Abstract摘要我们提出了一种新的语言表示模型,称为 BERT,即来自Transformer的双向编码器表示。与近期的语言表示模型(Peters et al., 2018a;Radford et al., 2018)不同,BERT 旨在通过在所有层中联合利用左右上下文,从无标注文本中预训练深度双向表示。因此,只需添加一个额外的输出层,即可对预训练的BERT模型进行微调,从而为包括问答和语言Inference在内的广泛任务创建最先进的模型,而无需对特定任务的架构进行实质性修改。

u013250861的博客 127

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

Elastic:开发者上手指南

你们好,我是Elastic的刘晓国。如果大家想开始学习Elastic的话,那么这里将是你理想的学习园地。在我的博客几乎涵盖了你想学习的许多方面。在这里,我来讲述一下作为一个菜鸟该如何阅读我的这些博客文章。 我们可以按照如下的步骤来学习: 1)Elasticsearch简介:对Elasticsearch做了一个简单的介绍 2)Elasticsearch中的一些重要概念:cluster,n..........................................................

Elastic 中国社区官方博客 17万+

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

Python轻量级科研元数据分析:清洗、术语浓缩与时序可视化

科研元数据是高度凝练的学术语义锚点,涵盖标题、摘要、关键词、年份、期刊等结构化字段。其核心价值在于无需全文解析即可揭示领域热点迁移、方法演进节奏与跨学科交叉趋势。技术原理上,依托pandas清洗归一、CountVectorizer+n-gram提取术语、TF-IDF抑制通用词干扰、pd.cut实现连续时间分组,并融合期刊影响力加权提升分析信度。这种轻量级方案规避了BERT微调、LDA主题建模等高成本NLP路径,在可复现性、调试透明度与工程落地性上优势显著。典型应用场景包括文献综述前期脉络梳理、基金本子领域态

liudaoru - 悟 557

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

AI应用开发全景指南:LLM原理+模型选型+MCP/Skills/Hooks三层工具生态,入门必读

2024-2026 年,AI 领域发生了根本性变化:大模型能力从"能用"到"好用",开发门槛从"需要 PhD + GPU 集群"降到"会调 API 就能搭应用"。对后端开发来说,这不是可选项——AI 应用开发正在成为和数据库、缓存、消息队列一样的基础技能。层次描述能力定位用过 AI 工具“我用 ChatGPT/Claude 写代码”基础操作系统化使用 AI“AI 辅助全流程:需求→设计→编码→审查”有方法论基于大模型开发应用“我用 Agent + RAG 搭建了 AI 应用”

qq_35201802的博客 688

Codeforces Round #485 (Div. 2)

A.暴力乱搞 B.找规律,会发现(2,3)(2,4)以及有一个数是1的时候是需要特判的,其余情况下小的那个数的幂次方总是比较大。 #include&lt;cstdio&gt; #include&lt;iostream&gt; using namespace std; int main() { int x,y; cin&gt;&gt;x&gt;&gt;y; if(x=...

HumveeA6的博客 454

Codeforces Round #489 (Div. 2)

A. 稍加思考就会发现其实只要知道其中非0的不同的数有多少个就可以了。 #include&lt;cstdio&gt; #include&lt;iostream&gt; #include&lt;set&gt; using namespace std; int main() { int n,i,j; set&lt;int&gt;s; cin&gt;&gt;n; ...

HumveeA6的博客 340

Codeforces 466 Div2

A签到题略过。 B虽然不难但仔细讨论起来还挺麻烦的,不想清楚容易GG。 #include&lt;cstdio&gt; #include&lt;iostream&gt; using namespace std; int main() { long long n,k,a,b,i,j; cin&gt;&gt;n&gt;&gt;k&gt;&gt;a&gt;&gt;b; lo...

HumveeA6的博客 338

2018校赛网络预选赛

A. 水题,set查重从小到大扫描即可。 #include&lt;cstdio&gt; #include&lt;iostream&gt; #include&lt;algorithm&gt; #include&lt;set&gt; using namespace std; int main() { int t,i,j,k,n,num[200005]; cin&gt;&gt...

HumveeA6的博客 326

Codeforces 467 Div2 D

题目非常显然可以用记忆化搜索来做,但是有几个地方需要考虑。首先是每搜到一个点的时候需要知道这个点是谁在走,虽然说可以通过记录步数来判断,但是记忆化的时候需要分开来记忆,并不是很好办。解决方案是拆点,也就是把原来的点v拆成v&lt;&lt;1(P走这个点)和v&lt;&lt;1|1(V走这个点), 如果原图中有一条u-&gt;v的边,那么我们连两条边: (u,0)-&gt;(v,1) &amp;...

HumveeA6的博客 320
上一篇: 字符串的最小表示法(模板)
下一篇: Educational Codeforces Round 38 (Rated for Div. 2)
HumveeA6
博客等级 码龄17年 12粉丝 175原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值