CodeForces 128B [ 后缀树 ]

本文介绍了一种使用后缀树解决求字符串中第K大子串问题的方法。通过构建后缀树并利用栈存储串信息,可以在O(N)的时间复杂度内找到答案。文中还探讨了如何处理多个询问的情况,并给出了完整的代码实现。

虽然是B题,正解是很简单的优先队列,但还是拿来当后缀树模板题敲了一下,O(N)求第K大子串;

这题只求了一个第K大串,但实际上,多个询问应该也是能搞的:

和后缀数组相仿,在后缀树上两端都是非叶子节点的边直接按 边上记录的[L,R]进行枚举遍历,是O(N)复杂度的,在遍历时用一个栈存储串的信息,是可以做到的。

然后就是第K大串可能在叶子节点向上的连边中,这时候因为这条边的一个字符的价值只有1(价值代表单位字符每多一个count增大多少,这里的count代表现在枚举到的串是第几大,价值为这条边底下的叶子节点数),所以可以直接算,或者打个标记之类的,就可以得到啦~

所以:如果询问的第K大串是有重复的(即这个串在所有子串出现了至少两次),那么询问M次,其复杂度是 O(MAX(size*N,M)),size是字符集大小;

如果有单一的存在,复杂度为O(max(Σ(单一串长),原来的复杂度));


总结了这么多,这题的代码如下:

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<math.h>
#include<iostream>
#include<stdlib.h>
#include<set>
#include<map>
#include<queue>
#include<vector>
#include<bitset>
#pragma comment(linker, "/STACK:1024000000,1024000000")
template <class T>
bool scanff(T &ret){ //Faster Input
    char c; int sgn; T bit=0.1;
    if(c=getchar(),c==EOF) return 0;
    while(c!='-'&&c!='.'&&(c<'0'||c>'9')) c=getchar();
    sgn=(c=='-')?-1:1;
    ret=(c=='-')?0:(c-'0');
    while(c=getchar(),c>='0'&&c<='9') ret=ret*10+(c-'0');
    if(c==' '||c=='\n'){ ret*=sgn; return 1; }
    while(c=getchar(),c>='0'&&c<='9') ret+=(c-'0')*bit,bit/=10;
    ret*=sgn;
    return 1;
}
#define inf 1073741823
#define llinf 4611686018427387903LL
#define PI acos(-1.0)
#define lth (th<<1)
#define rth (th<<1|1)
#define rep(i,a,b) for(int i=int(a);i<=int(b);i++)
#define drep(i,a,b) for(int i=int(a);i>=int(b);i--)
#define gson(i,root) for(int i=ptx[root];~i;i=ed[i].next)
#define tdata int testnum;scanff(testnum);for(int cas=1;cas<=testnum;cas++)
#define mem(x,val) memset(x,val,sizeof(x))
#define mkp(a,b) make_pair(a,b)
#define findx(x) lower_bound(b+1,b+1+bn,x)-b
#define pb(x) push_back(x)
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;


#define SIZE 27
struct suffixtree{
        struct node{
        int l,r,point,a[SIZE];
        void init(){
            mem(a,0);
            point=l=0;
            r=-1;
        }
    }T[400011];
    char s[400011];
    int actnode,actride,actline;
    int rest,total,temp,linker,len;
    void builtleaf(int root,int line){
        total++;
        T[total].init();
        T[total].l=len;
        T[total].r=100000001;
        T[root].a[line]=total;
        rest--;
    }
    bool pd_ride(int next){
        temp=T[next].r-T[next].l+1;
        if(actride>=temp){
            actride-=temp;
            actnode=next;
            actline+=temp;
            return true;
        }
        return false;
    }
    void link(int x){
        if(linker>0)T[linker].point=x;
        linker=x;
    }
    void insert(int wait){
        s[++len]=wait;
        rest++;
        linker=0;
        while(rest>0){
            if(actride==0)actline=len;
            if(T[actnode].a[s[actline]]==0){ 
                builtleaf(actnode,s[actline]);
                link(actnode);
            }
            else{
                int next=T[actnode].a[s[actline]];
                if(pd_ride(next))continue;
                if(s[T[next].l+actride]==wait){
                    actride++;
                    link(actnode);
                    break;
                }  
                total++;
                T[total].init();
                T[actnode].a[s[actline]]=total;
                T[total].l=T[next].l;
                T[total].r=T[next].l+actride-1;
                T[next].l+=actride;
                T[total].a[s[T[next].l]]=next;
                link(total);
                builtleaf(total,wait);
            }
            if(actnode==0&&actride>0){
                actride--;
                actline=len-rest+1;
            }
            else actnode=T[actnode].point;
        }
    }
    void clear(){
        total=0;
        len=0;
        T[0].init();
        actnode=actride=actline=0;
    }
    void debug(){
        rep(i,0,total){
            printf("T[%d] (%d %d)\n",i,T[i].l,T[i].r);
        }
    }
}st; 

#define NN 400400
char s[NN];
ll cot[NN];
ll sum;
ll k;
int len;

ll getcot(int x){
    ll temp=0;
    ll bj=1;
    rep(i,0,26){
        if(st.T[x].a[i]){
            bj=0;
            temp+=getcot(st.T[x].a[i]);
        }
    }
    cot[x]=temp+bj;
    return cot[x];
}
ll edr,edx;
int alen=0;
char ans[NN];
void dfs(int x){
    sum+=(ll)(min(st.T[x].r,len)-st.T[x].l+1)*cot[x];
    if(sum>=k){
        edx=x;
        edr=min(ll(st.T[x].r),ll(len));
        while(sum-cot[x]>=k){
            sum-=cot[x];
            edr--;
        }
        //printf("%lld %lld\n",edx,edr);
        //return;
    }
    rep(i,0,26){
        if(st.T[x].a[i]&&sum<k){
            dfs(st.T[x].a[i]);
        }
    }
    if(sum>=k){
        drep(i,min(edr,ll(st.T[x].r)),st.T[x].l){
            ans[alen++]=s[i];
        }
    }
}




main(){
    st.clear();
    scanf("%s",s+1);
    len=strlen(s+1);
    rep(i,1,len)st.insert(s[i]-'a');
    st.insert(26);
    scanf("%lld",&k);
    if(ll(len)*ll(len+1)/2LL<k){
        printf("No such line.\n");
        return 0;
    }
    getcot(0);
    dfs(0);
    ans[alen]=0;
    reverse(ans,ans+alen);
    printf("%s\n",ans);
}



Codeforces Gym 101630 是一场编程竞赛,通常包含多个算法挑战问题。这些问题往往涉及数据结构、算法设计、数学建模等多个方面,旨在测试参赛者的编程能力和解决问题的能力。 以下是一些可能出现在 Codeforces Gym 101630 中的题目类型及解决方案概述: ### 题目类型 1. **动态规划(DP)** 动态规划是编程竞赛中常见的题型之一。问题通常要求找到某种最优解,例如最小路径和、最长递增子序列等。解决这类问题的关键在于状态定义和转移方程的设计[^1]。 2. **图论** 图论问题包括最短路径、最小生成树、网络流等。例如,Dijkstra 算法用于求解单源最短路径问题,而 Kruskal 或 Prim 算法则常用于最小生成树问题[^1]。 3. **字符串处理** 字符串问题可能涉及模式匹配、后缀数组、自动机等高级技巧。KMP 算法和 Trie 树是解决此类问题的常用工具[^1]。 4. **数论与组合数学** 这类问题通常需要对质数、模运算、排列组合等有深入的理解。例如,快速幂算法可以用来高效计算大数的模幂运算[^1]。 5. **几何** 几何问题可能涉及点、线、多边形的计算,如判断点是否在多边形内部、计算两个圆的交点等。向量运算和坐标变换是解决几何问题的基础[^1]。 ### 解决方案示例 #### 示例问题:动态规划 - 最长递增子序列 ```python def longest_increasing_subsequence(nums): if not nums: return 0 dp = [1] * len(nums) for i in range(len(nums)): for j in range(i): if nums[i] > nums[j]: dp[i] = max(dp[i], dp[j] + 1) return max(dp) # 示例输入 nums = [10, 9, 2, 5, 3, 7, 101, 18] print(longest_increasing_subsequence(nums)) # 输出: 4 ``` #### 示例问题:图论 - Dijkstra 算法 ```python import heapq def dijkstra(graph, start): distances = {node: float('infinity') for node in graph} distances[start] = 0 priority_queue = [(0, start)] while priority_queue: current_distance, current_node = heapq.heappop(priority_queue) if current_distance > distances[current_node]: continue for neighbor, weight in graph[current_node].items(): distance = current_distance + weight if distance < distances[neighbor]: distances[neighbor] = distance heapq.heappush(priority_queue, (distance, neighbor)) return distances # 示例输入 graph = { 'A': {'B': 1, 'C': 4}, 'B': {'A': 1, 'C': 2, 'D': 5}, 'C': {'A': 4, 'B': 2, 'D': 1}, 'D': {'B': 5, 'C': 1} } start = 'A' print(dijkstra(graph, start)) # 输出: {'A': 0, 'B': 1, 'C': 3, 'D': 4} ``` #### 示例问题:字符串处理 - KMP 算法 ```python def kmp_failure_function(pattern): m = len(pattern) lps = [0] * m length = 0 # length of the previous longest prefix suffix i = 1 while i < m: if pattern[i] == pattern[length]: length += 1 lps[i] = length i += 1 else: if length != 0: length = lps[length - 1] else: lps[i] = 0 i += 1 return lps def kmp_search(text, pattern): n = len(text) m = len(pattern) lps = kmp_failure_function(pattern) i = 0 # index for text j = 0 # index for pattern while i < n: if pattern[j] == text[i]: i += 1 j += 1 if j == m: print("Pattern found at index", i - j) j = lps[j - 1] elif i < n and pattern[j] != text[i]: if j != 0: j = lps[j - 1] else: i += 1 # 示例输入 text = "ABABDABACDABABCABAB" pattern = "ABABCABAB" kmp_search(text, pattern) # 输出: Pattern found at index 10 ``` ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值