后缀数组
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 | #define SUFFIX_ARRAY_PFXDBL #ifdef SUFFIX_ARRAY_PFXDBL namespace SA{ /* Suffix Array, Prefix Doubling Method <SUBSCRIPT_START_FROM,SIZE> MAXN : Maximum length for string s : String for building suffix array <0,MAXN> t,t2,c : [Auxiliary] buffers <0,MAXN> n : Length of string sa : Suffix Array, Lexicographically ith suffix starts from. <0,MAXN> rank : Suffix Rank, rank of the suffix starts from i <0,MAXN> height : LCP of suffix starts from sa[i] and sa[i-1] <1,MAXN> build_sa(int m) : m is the size of character set("character" being 0~m-1), should set s and n previously. getHeight() : Calculate height and rank array accordingly. #define REP(i,t) for(int i = 0;i < t; i++) #define REP_R(i,t) for(int i = t-1;i >= 0; i--) #define REP_1(i,t) for(int i = 1;i <= t; i++) #define REP_1R(i,t) for(int i = t;i >= 1; i--) Time : O(nlogn) Space : 7n */ const int MAXN = 10005; char s[MAXN]; int sa[MAXN],t[MAXN],t2[MAXN],c[MAXN],rank[MAXN],height[MAXN],n; void build_sa( int m){ int *x = t,*y = t2; //第一次基数排序 REP(i, m) c[i] = 0; //重置排序数组 REP(i, n) c[x[i]=s[i]]++; //拷贝字符串内容到x缓冲区,并在基数排序数组中统计字符数量 REP_1(i, m-1) c[i] += c[i-1]; //基数排序数组求前缀和以算出排名 REP_R(i, n-1) sa[--c[x[i]]] = i; //从后往前顺次查询后缀的排名写入SA(字符相同的时候,长度小的靠前,长度大的靠后) //此时的x数组可以看成是保存了各个位置开始的后缀对应的第一基准 for ( int k = 1;k <= n; k <<= 1){ //第k次基数排序,单组长度2^k int p = 0; //利用SA排序第二基准,y存储的是第二基准排序之后的后缀开始位置 for ( int i = n-k;i < n; i++) y[p++] = i; //长度没达到k,这种只能看第一基准,第二基准视为0 REP(i, n) if (sa[i] >= k) y[p++] = sa[i]-k; //从前往后扫SA,即从字典序小的后缀开始扫,如果这个后缀的开始位置>=k的话, //这个后缀在sa中的结果实际上是i-k的第二基准 //排序第一基准 REP(i, m) c[i] = 0; //重置排序数组 REP(i, n) c[x[y[i]]]++; //按第二基准顺序读取第一基准进行统计 REP(i, m) c[i] += c[i-1]; //基数排序数组求前缀和以算出排名 REP_R(i, n-1) sa[--c[x[y[i]]]] = y[i]; //按第二基准从后往前顺次查询后缀的排名写入SA //准备下一次排序的第一基准序列x //第二基准排序结果y没有用了,上一次的第一基准序列x还有用,直接互换 swap(x, y); //现在y是上一次的第一基准序列 p = 1; x[sa[0]] = 0; //第一基准从0开始 //现在的sa是排好序的,从小到大查询,如果两项无法分出先后,就令成统一基准,否则基准自增 //如果两个后缀的第一关键字和第二关键字一样,说明他们的排名一样 REP_1(i, n-1) x[sa[i]] = ((y[sa[i-1]] == y[sa[i]]) && (y[sa[i-1]+k]==y[sa[i]+k]))? p-1 : p++; if (p >= n) break ; //如果基准数已达n,说明排序完成,所有后缀已经分出先后 m = p; //下一轮排序的基准数是p } } void getHeight(){ int i,j,k = 0; REP(i, n) rank[sa[i]] = i; for (i = 0;i < n; height[rank[i++]] = k) for (k?k--:0,j = sa[rank[i]-1];s[i+k]==s[j+k];k++); return ; } } #endif |
┆ 凉 ┆ 暖 ┆ 降 ┆ 等 ┆ 幸 ┆ 我 ┆ 我 ┆ 里 ┆ 将 ┆ ┆ 可 ┆ 有 ┆ 谦 ┆ 戮 ┆ 那 ┆ ┆ 大 ┆ ┆ 始 ┆ 然 ┆
┆ 薄 ┆ 一 ┆ 临 ┆ 你 ┆ 的 ┆ 还 ┆ 没 ┆ ┆ 来 ┆ ┆ 是 ┆ 来 ┆ 逊 ┆ 没 ┆ 些 ┆ ┆ 雁 ┆ ┆ 终 ┆ 而 ┆
┆ ┆ 暖 ┆ ┆ 如 ┆ 地 ┆ 站 ┆ 有 ┆ ┆ 也 ┆ ┆ 我 ┆ ┆ 的 ┆ 有 ┆ 精 ┆ ┆ 也 ┆ ┆ 没 ┆ 你 ┆
┆ ┆ 这 ┆ ┆ 试 ┆ 方 ┆ 在 ┆ 逃 ┆ ┆ 会 ┆ ┆ 在 ┆ ┆ 清 ┆ 来 ┆ 准 ┆ ┆ 没 ┆ ┆ 有 ┆ 没 ┆
┆ ┆ 生 ┆ ┆ 探 ┆ ┆ 最 ┆ 避 ┆ ┆ 在 ┆ ┆ 这 ┆ ┆ 晨 ┆ ┆ 的 ┆ ┆ 有 ┆ ┆ 来 ┆ 有 ┆
┆ ┆ 之 ┆ ┆ 般 ┆ ┆ 不 ┆ ┆ ┆ 这 ┆ ┆ 里 ┆ ┆ 没 ┆ ┆ 杀 ┆ ┆ 来 ┆ ┆ ┆ 来 ┆
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)