2018焦作网络赛 H. String and Times(出现次数在[L,R]内的子串数,后缀自动机)

本文介绍了一种基于串模式匹配的算法,用于解决给定串S中有多少个子串出现次数在[A,B]区间内的问题。通过构建SAM结构,实现对字符串的高效处理,最后通过拓扑计算得出每种串出现的次数,从而统计满足条件的子串数量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意:

给定串S,和两个整数A,B,
问S有多少个子串出现次数在[A,B]内。

数据范围:sum(|S|)<=2e6,所有字符由大写字母组成

解法:

标记endpos,然后拓扑计算出每种串出现的次数,然后就可以直接统计答案了。

ps:
题面上面只写了sum(|S|)<=2e6,但是没说单个串的长度最大最大,
开2e6的超内存了,开1e5直接越界超时,开2e5才过,
貌似是赛中数据范围没给出锅了?

code:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxm=2e5+5;
char s[maxm];
int n;
int L,R;
struct SAM{
    int ch[maxm][26];
    int fa[maxm],l[maxm];//l[]是等价类的最长字符串长度len
    int last=1,tot=1;//tot是节点数量
    //
    int cnt[maxm];
    int idx[maxm];
    int sz[maxm];
    //
    void add(int c){//传入s[i]-'a'而不是s[i]
        int p=last,np=++tot;last=np;l[np]=l[p]+1;
        for(;p&&!ch[p][c];p=fa[p])ch[p][c]=np;
        if(!p)fa[np]=1;
        else{
            int q=ch[p][c];
            if(l[p]+1==l[q])fa[np]=q;
            else{
                int nq=++tot;l[nq]=l[p]+1;
                memcpy(ch[nq],ch[q],sizeof ch[q]);
                fa[nq]=fa[q];fa[q]=fa[np]=nq;
                for(;ch[p][c]==q;p=fa[p])ch[p][c]=nq;
            }
        }
        sz[np]=1;
    }
    void init(){
        for(int i=1;i<=tot;i++){
            memset(ch[i],0,sizeof ch[i]);
            fa[i]=l[i]=0;
            sz[i]=0;
            cnt[i]=0;
        }
        last=tot=1;
    }
    ll solve(){
        //基数排序
        for(int i=1;i<=tot;i++)cnt[l[i]]++;
        for(int i=1;i<=tot;i++)cnt[i]+=cnt[i-1];
        for(int i=1;i<=tot;i++)idx[cnt[l[i]]--]=i;
        //逆向拓扑
        for(int i=tot;i>=1;i--){
            int x=idx[i];
            sz[fa[x]]+=sz[x];
        }
        //
        ll ans=0;
        for(int i=1;i<=tot;i++){
            int x=idx[i];
            if(sz[x]>=L&&sz[x]<=R){
                ans+=l[x]-l[fa[x]];
            }
        }
        return ans;
    }
}S;
signed main(){
    while(scanf("%s",s+1)!=EOF){
        S.init();
        scanf("%d%d",&L,&R);
        n=strlen(s+1);
        for(int i=1;i<=n;i++){
            S.add(s[i]-'A');
        }
        ll ans=S.solve();
        printf("%lld\n",ans);
    }
    return 0;
}

``` #include <bits/stdc++.h> using namespace std; int n; string s[1000005],t; struct trie { int nxt[1000005][26],times[1000005]; int sum[1000005],fail[1000005],in[1000005],sz; queue <int> q; int pos(string s,int len) { int p = 0; for (int i = 0;i < len;i++) { if (!nxt[p][s[i]-'a']) return 0; p = nxt[p][s[i]-'a']; } return p; } void insert(string s,int len) { int p = 0; for (int i = 0;i < len;i++) { if (!nxt[p][s[i]-'a']) nxt[p][s[i]-'a'] = ++sz; p = nxt[p][s[i]-'a']; } times[p]++; } void kmp() { memset(fail,0,sizeof(fail)); for (int i = 0;i < 26;i++) if (nxt[0][i]) q.push(nxt[0][i]); while (!q.empty()) { int i = q.front(); q.pop(); for (int j = 0;j < 26;j++) if (nxt[i][j]) { int p = fail[i]; while (p>0 && !nxt[p][j]) p = fail[p]; if (nxt[p][j]) p = nxt[p][j]; fail[nxt[i][j]] = p,in[p]++; q.push(nxt[i][j]); } } } void search(string s,int len) { memset(sum,0,sizeof(sum)); int p = 0; for (int i = 0;i < len;i++) { while (!nxt[p][s[i]-'a'] && p>0) p = fail[p]; if (!nxt[p][s[i]-'a']) continue; p = nxt[p][s[i]-'a'],sum[p]++; } for (int i = 0;i < sz;i++) if (in[i] == 0) q.push(i); while (!q.empty()) { int f = q.front(); q.pop(); while (f) { if (in[fail[f]] == 1) q.push(in[fail[f]]); sum[fail[f]] += sum[f],in[fail[f]]--,f = fail[f]; } } } void clear() { sz = 0; memset(nxt,0,sizeof(nxt)); memset(times,0,sizeof(times)); } } tree; int main() { cin >> n; for (int i = 1;i <= n;i++) cin >> s[i]; for (int i = 1;i <= n;i++) tree.insert(s[i],s[i].length()); cin >> t; tree.kmp(),tree.search(t,t.length()); for (int i = 1;i <= n;i++) cout << tree.sum[tree.pos(s[i],s[i].length())] << endl; return 0; }```题目描述 给你一个文本 S 和 n 个模式 T1∼n​,请你分别求出每个模式 Ti​ 在 S 中出现的次数。 输入格式 第一行包含一个正整 n 表示模式的个。 接下来 n 行,第 i 行包含一个由小写英文字母构成的非空字符 Ti​。 最后一行包含一个由小写英文字母构成的非空字符 S。 据不保证任意两个模式不相同。 输出格式 输出包含 n 行,其中第 i 行包含一个非负整表示 Ti​ 在 S 中出现的次数。 输入样例: 5 a bb aa abaa abaaa abaaabaa 输出: 6 0 3 2 1 样例输出不对,请分析代码哪里有错误
03-10
内容概要:本文详细介绍了基于结构不变补偿的电液伺服系统低阶线性主动干扰抑制控制(ADRC)方法的实现过程。首先定义了电液伺服系统的基本参,并实现了结构不变补偿(SIC)函,通过补偿非线性项和干扰,将原始系统转化为一阶积分链结构。接着,设计了低阶线性ADRC控制器,包含扩展状态观测器(ESO)和控制律,用于估计系统状态和总干扰,并实现简单有效的控制。文章还展示了系统仿真与对比实验,对比了低阶ADRC与传统PID控制器的性能,证明了ADRC在处理系统非线性和外部干扰方面的优越性。此外,文章深入分析了参调整与稳定性,提出了频域稳定性分析和b0参调整方法,确保系统在参不确定性下的鲁棒稳定性。最后,文章通过综合实验验证了该方法的有效性,并提供了参敏感性分析和工程实用性指导。 适合人群:具备一定自动化控制基础,特别是对电液伺服系统和主动干扰抑制控制感兴趣的科研人员和工程师。 使用场景及目标:①理解电液伺服系统的建模与控制方法;②掌握低阶线性ADRC的设计原理和实现步骤;③学习如何通过结构不变补偿简化复杂系统的控制设计;④进行系统仿真与实验验证,评估不同控制方法的性能;⑤掌握参调整与稳定性分析技巧,确保控制系统在实际应用中的可靠性和鲁棒性。 阅读建议:本文内容详尽,涉及多个控制理论和技术细节。读者应首先理解电液伺服系统的基本原理和ADRC的核心思想,然后逐步深入学习SIC补偿、ESO设计、控制律实现等内容。同时,结合提供的代码示例进行实践操作,通过调整参和运行仿真,加深对理论的理解。对于希望进一步探索的读者,可以关注文中提到的高级话题,如频域稳定性分析、参敏感性分析等,以提升对系统的全面掌控能力。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值