制作好的网站必须申请后台登录wordpress
web/
2025/10/1 13:34:15/
文章来源:
制作好的网站必须申请,后台登录wordpress,学校网页制作模板,企业联系电话字符串哈希 KMP 基本
字符串哈希
理论
将一个字符串转成一个数字#xff0c;可以快速比较两个字符串是否相同等。要求为#xff1a;相同字符串哈希值相同#xff0c;不同字符串哈希值尽量不相同。
映射方法通常采用多项式哈希方法#xff0c;很像进制转换。假设字符串为…字符串哈希 KMP 基本
字符串哈希
理论
将一个字符串转成一个数字可以快速比较两个字符串是否相同等。要求为相同字符串哈希值相同不同字符串哈希值尽量不相同。
映射方法通常采用多项式哈希方法很像进制转换。假设字符串为 S S S其哈希值为 f ( S ) f(S) f(S)。定义一个小的正整数 b a s e base base比如说 27 , 131 27,131 27,131表示将 S S S 视为 b a s e base base 进制下的数字。将字符串中涉及到的每个单独的字符 S i S_i Si转换为一个数字 x i x_i xi比如说 a \texttt{a} a 为 0 0 0 c \texttt{c} c 为 2 2 2后用秦久韶算法把 S S S 转换后得到的数串 { x 1 , x 2 , … , x n } \{x_1,x_2,\dots,x_n\} {x1,x2,…,xn} 变成 S S S 的哈希值 f ( S ) ( … ( ( ( ( x 1 × b a s e ) x 2 ) × b a s e ) x 3 ) × b a s e ) … ) × b a s e ) x n f(S)(\dots((((x_1\times base)x_2)\times base)x_3)\times base)\dots)\times base)x_n f(S)(…((((x1×base)x2)×base)x3)×base)…)×base)xn 举个例子若 S xyz S\texttt{xyz} Sxyz则 f ( S ) 23 × b a s e 2 24 × b a s e 25 f(S)23\times base^224\times base25 f(S)23×base224×base25。由于 f ( S ) f(S) f(S) 通常会很大long long 会存不下所以要么加上 unsigned 自然溢出要么开一个大质数 P P P 把 f ( S ) f(S) f(S) 对 P P P 取模。
如果有两个不同的字符串 S , T S,T S,T在某种哈希方法中 f ( S ) f ( T ) f(S)f(T) f(S)f(T) 则称为发生了哈希冲突。哈希函数的目的就是尽可能降低发生哈希冲突的概率。如果不采用任何优化手段当 b a s e base base 在 [ 0 , P ) [0,P) [0,P) 中均匀随机选取则发生哈希冲突的概率大约为 l − 1 P \cfrac{l-1}{P} Pl−1其中 l max ( ∣ S ∣ , ∣ T ∣ ) l\max(|S|,|T|) lmax(∣S∣,∣T∣)。
如果字符串总数大于哈希值的范围( P P P)由鸽巢原理可知至少有两个字符串发生哈希冲突。所以最朴素的方法就是增加 P P P。但根据生日悖论 P P P 不够大时只需要很少的字符串就很容易发生哈希冲突。以 P 1 0 9 7 P10^97 P1097 为例当有 1 0 5 10^5 105 个不同的字符串时发生哈希冲突的概率高达 99.3263 % 99.3263\% 99.3263%。
解决哈希冲突的常见方法
拉链法用一条链把相同哈希值的元素挂在一起查询时算出哈希值后顺着挂着的链遍历一遍查找该字符串。开放地址法如果先后遇到两个字符串 S , T S,T S,T f ( S ) f ( T ) f(S)f(T) f(S)f(T)则让 f ( S ) f(S) f(S) 保持不变 f ( T ) f(T) f(T) 去寻找新的位置填入比如说 f ( T ) ← f ( T ) 1 f(T)\gets f(T)1 f(T)←f(T)1 去探测。再哈希法准备多个备用哈希函数当发生冲突时使用备用的哈希函数再次计算哈希值直到不发生冲突后将字符串映射为该哈希值。公共溢出法准备一个额外的空间作为公共溢出区当 S S S 发生哈希冲突时就将 S S S 扔进公共溢出区的末尾。查询时扫描整个公共溢出区即可。多模哈希用多个不同的 ( b a s e , P ) (base,P) (base,P) 计算哈希值当 f ( S ) ≠ f ( T ) f(S)\ne f(T) f(S)f(T) 和 g ( S ) ≠ g ( T ) g(S)\ne g(T) g(S)g(T) 同时成立时才判断 S ≠ T S\ne T ST其中 g g g 是另一个哈希函数。双哈希最为常用。
如何 O ( 1 ) O(1) O(1) 得到 S S S 的某个子串 [ l , r ] [l,r] [l,r] 的哈希值我们在计算 f ( S ) f(S) f(S) 时多记录一下 S S S 的前缀哈希值 h h h h i h i − 1 × b a s e x i h_ih_{i-1}\times basex_i hihi−1×basexi其中 x i x_i xi 表示 S i S_i Si 的数字形式。则 f ( S l … r ) f(S_{l\dots r}) f(Sl…r) 即为 h r − h l − 1 × b a s e r − l 1 h_r-h_{l-1}\times base^{r-l1} hr−hl−1×baser−l1。可以提前预处理出 b a s e base base 的若干次幂。
在 OI 赛场上建议使用不同寻常的质数作为 P P P否则很有可能会被卡掉。给出一个双哈希模板
// 假设字符串只有小写字母
#define ll long long
const int BASE1 27,P1 998244353;
const int BASE2 131,P2 1e9 7;
int F(char s[]) {int n strlen(s 1); ll res 0;for (int i 1;i n;i )(res (res * BASE1 % P1) s[i] - a) % P1;return res;
}
int G(char s[]) {int n strlen(s 1); ll res 0;for (int i 1;i n;i )(res (res * BASE2 % P1) s[i] - a) % P2;return res;
}
bool checkDifferent(char s[],char t[]) { return F(s) ! F(t) || G(s) ! G(t); 允许失配的匹配 给定长度为 n n n 的文本串 S S S 和长度为 m m m 的模式串 T T T。定义 S S S 的子串 S ′ S S′ 与 T T T 匹配当且仅当 ∣ S ′ ∣ ∣ T ∣ |S||T| ∣S′∣∣T∣ 且最多有 k k k 个位置字符不同。求能够与 T T T 匹配的 S ′ S S′ 的个数。 1 ≤ n , m ≤ 1 0 6 1\le n,m\le10^6 1≤n,m≤106 0 ≤ k ≤ 5 0\le k\le 5 0≤k≤5。 哈希二分。先枚举 S S S 中所有长度为 m m m 的子串 S ′ S S′尝试与 T T T 进行匹配。求出 S ′ S S′ 和 T T T 的前缀哈希值 s u m S ′ , s u m T sumS,sumT sumS′,sumT S ′ S S′ 的前缀哈希用 S S S 的前缀哈希计算得到然后二分出 S ′ , T S,T S′,T 第一个不同的位置如果 s u m S m i d ′ s u m T m i d sumS_{mid}sumT_{mid} sumSmid′sumTmid 相同则往后找否则往前找最终将 S ′ S S′ 和 T T T 中二分得到的失配位置及之前的部分全部删除继续二分下一个不同的位置。如果二分到第 k 1 k1 k1 轮发现仍有失配位置存在则说明 S ′ S S′ 无法匹配 T T T否则答案加一。
二分至多进行 ( n − m 1 ) × k (n-m1)\times k (n−m1)×k 轮加上开始时计算的 s u m S , s u m T sumS,sumT sumS,sumT 的 O ( n m ) O(nm) O(nm)总时间复杂度为 O ( m k n log m ) O(mkn\log m) O(mknlogm)。
最长回文子串 给定长度为 n n n 的字符串 S S S求其中的最长回文子串。 O ( n log n ) O(n\log n) O(nlogn) 做法预处理出 S S S 正着的前缀哈希和倒着的前缀哈希。二分回文子串长度check 的时候枚举每个回文中心哈希判断两侧是否相等。 O ( n ) O(n) O(n) 做法记 R i R_i Ri 表示以 i i i 结尾的最长回文子串的长度最终答案即为 max { R i } ( 1 ≤ i ≤ n ) \max\{R_i\}\ (1\le i\le n) max{Ri} (1≤i≤n)。如果要以 i 1 i1 i1 作为结尾那么最优的 R i 1 R_{i1} Ri1 即为从以 i i i 结尾的最长回文子串的前一个字符与这段回文子串和 S i 1 S_{i1} Si1 拼起来组成一个长度为 R i 2 R_i2 Ri2 的回文子串前提是 S i 1 S_{i1} Si1 与这个字符相同。所以有一个性质 R i ≤ R i − 1 2 R_{i}\le R_{i-1}2 Ri≤Ri−12。然后我们只需要暴力从 R i − 1 2 R_{i-1}2 Ri−12 开始递减直到用哈希判出一个回文即可。
最长公共子串 给定 m m m 个总长不超过 n n n 的非空字符串查找所有字符串的最长公共子串。其中 1 ≤ n , m ≤ 1 0 6 1\le n,m\le10^6 1≤n,m≤106。 如果存在长度为 k k k 的最长公共子串那么长度为 k − 1 k-1 k−1 的子串也一定存在挖掉一个字符即可。所以可以二分这个子串的长度check 部分即为将所有字符串中长度为 k k k 的子串全部求一遍哈希值前缀哈希然后将求得的值分别放入 n n n 个表中最终如果这些表存在交集则该答案合法。时间复杂度 O ( n log n m ) O(n\log \cfrac{n}{m}) O(nlogmn)。
KMP
理论
用于计算给定一个文本串 S S S长串和一个模式串 T T T短串计算 T T T 在 S S S 中的出现次数/位置。适用于 T T T 不变的情况。令 n n n 为 ∣ S ∣ |S| ∣S∣ m m m 为 ∣ T ∣ |T| ∣T∣暴力很显然是 O ( n m ) O(nm) O(nm)。
暴力中其实多了很多冗余的比较。KMP 中就对 T T T 多求了个 n x t nxt nxt 数组 n x t i nxt_i nxti 表示 T T T 中到 i i i 为止的子串的真前缀和真后缀最大相同的前缀位置而字符串中真前后缀相同的部分称为原串的一个 border。例如模式串 T aabaabc T\texttt{aabaabc} Taabaabc对其计算 n x t { 0 , 1 , 0 , 1 , 2 , 3 , 0 } nxt\{0,1,0,1,2,3,0\} nxt{0,1,0,1,2,3,0}。
假设 S aabaaabaabaabca S\texttt{aabaaabaabaabca} Saabaaabaabaabca则 KMP 的匹配过程即为 T 1 … 5 T_{1\dots5} T1…5 中的字符都匹配成功但 T 6 T_6 T6 匹配失败 S 6 a , T 6 b , S 6 ≠ T 6 S_6\texttt{a},T_6\texttt{b},S_6\ne T_6 S6a,T6b,S6T6根据当前已经匹配成功的字符个数 n o w 5 now5 now5移动 T 1 T_1 T1 与 S 4 S_4 S4 对齐即 n o w ← n x t 5 now\gets nxt_{5} now←nxt5表示匹配成功的字符个数从 5 5 5 变成 2 2 2。继续匹配直到 T 1 … 2 T_{1\dots 2} T1…2 都匹配成功但 T 3 T_3 T3 失配 S 6 a , T 3 b S_6\texttt{a},T_3\texttt{b} S6a,T3b根据当前已经匹配成功的字符个数 n o w 2 now2 now2移动 T 1 T_1 T1 与 S 5 S_5 S5 对齐即 n o w ← n x t 2 now\gets nxt_{2} now←nxt2表示匹配成功的字符个数从 2 2 2 变成 1 1 1。继续匹配直到 T 1 … 6 T_{1\dots 6} T1…6 都匹配成功但 T 7 T_7 T7 失配 S 1 1 a , T 7 c S_11\texttt{a},T_7\texttt{c} S11a,T7c根据当前已经匹配成功的字符个数 n o w 6 now6 now6移动 T 1 T_1 T1 与 S 8 S_8 S8 对齐即 n o w ← n x t 6 now\gets nxt_{6} now←nxt6表示匹配成功的字符个数从 6 6 6 变成 3 3 3。继续匹配就发现匹配成功了。
综上匹配操作即为令 n o w now now 表示已成功匹配的字符个数 i i i 表示 S S S 匹配到的位置初始 n o w 0 , i 1 now0,i1 now0,i1检查 S S S 中的当前字符和 T n o w 1 T_{now1} Tnow1 是否相同若相同则 n o w ← n o w 1 , i ← i 1 now\gets now1,i\gets i1 now←now1,i←i1否则
如果 n o w ≠ 0 now\ne 0 now0则 n o w ← n x t n o w now\gets nxt_{now} now←nxtnow 且 i i i 不变如果 n o w 0 now0 now0则 i ← i 1 i\gets i1 i←i1。
重复匹配直到 n o w ∣ T ∣ now|T| now∣T∣ 或 i ≥ ∣ S ∣ i\ge |S| i≥∣S∣。时间复杂度 O ( n ) O(n) O(n)证明略。
怎么算 n x t nxt nxt 数组呢这相当于 T T T 和 T T T 自己进行匹配。逐位向后扩展当失配时利用已经得到的 n x t nxt nxt 跳。仍设两个变量 n o w , i now,i now,i初始 n o w 0 now0 now0注意初始时 i 2 i2 i2显然 n x t 1 0 nxt_10 nxt10当 T i T n x t 1 T_iT_{nxt1} TiTnxt1 时计算 n x t i n o w 1 nxt_inow1 nxtinow1 并且 n o w ← n o w 1 , i ← i 1 now\gets now1,i\gets i1 now←now1,i←i1否则
如果 n o w ≠ 0 now\ne 0 now0此时 n x t n o w nxt_{now} nxtnow 已经求出所以 n o w ← n x t n o w now\gets nxt_{now} now←nxtnow。如果 n o w 0 now0 now0则 n x t i 0 , i ← i 1 nxt_i0,i\gets i1 nxti0,i←i1。
时间复杂度 O ( m ) O(m) O(m)。给出一个模板
// 求 nxt
nxt[1] 0;
for (int i 2,now 0;i m;nxt[i] now,i ) {while (now T[i] ! T[now 1]) now nxt[now];if (T[i] T[now 1]) now ;
}
// 匹配这里仅输出第一个匹配成功的位置开头
for (int i 1,now 0;i n;i ) {while (now S[i] ! T[now 1]) now nxt[now];if (S[i] T[now 1]) now ;if (now m) {printf(%d,i - m 1);break;}
}前缀出现次数 给定长度为 n n n 的字符串 S S S 和长度为 m m m 的字符串 T T T统计 每个前缀 S 1 … i S_{1\dots i} S1…i 在 S S S 中的出现次数。每个前缀 S 1 … i S_{1\dots i} S1…i 在 T T T 中的出现次数。 在 oi-wiki 的这里讲得非常细所以偷个懒。
题目
上午
因为老师没给我开所以写不了一点qwq
下午
优秀拆分 95pts
令 f i f_i fi 表示以 i i i 结尾的形如 AA \texttt{AA} AA 的子串个数 g i g_i gi 表示以 i i i 开头的形如 BB \texttt{BB} BB 的子串个数。计算方法即为枚举每个长度为偶数的子串 [ L , R ] [L,R] [L,R]比较 [ L , m i d ] [L,mid] [L,mid] 和 [ m i d 1 , R ] [mid1,R] [mid1,R] 的哈希值是否相同。然后枚举 AA,BB \texttt{AA,BB} AA,BB 之间的分界线 i i i贡献即为 f i × g i 1 f_i\times g_{i1} fi×gi1。复杂度 O ( n 2 ) O(n^2) O(n2)瓶颈在于求 f , g f,g f,g。
动物园 n u m i num_i numi 其实就是求从以 i i i 结尾长度不超过 ⌊ i 2 ⌋ \lfloor\frac{i}{2}\rfloor ⌊2i⌋ 的最长 border 开始不断跳 n x t nxt nxt 直到 n x t i 0 nxt_i0 nxti0 所用的次数。令 n o w n x t i nownxt_i nownxti n o w now now 合法此时 S [ 1 , n o w ] S ( i − n o w , i ] ( 1 ) S[1,now]S(i-now,i]\ \ \ (1) S[1,now]S(i−now,i] (1)。如果 n x t n o w nxt_{now} nxtnow 有值那么说明 S [ 1 , n x t n o w ] S ( n o w − n x t n o w , n o w ] ( 2 ) S[1,nxt_{now}]S(now-nxt_{now},now]\ \ \ (2) S[1,nxtnow]S(now−nxtnow,now] (2) 根据 ( 1 ) (1) (1) 和 ( 2 ) (2) (2) 可知 S ( i − n o w , i − n o w n x t n o w ] S ( i − n x t n o w , i ] ( 3 ) S(i-now,i-nownxt_{now}]S(i-nxt_{now},i]\ \ \ (3) S(i−now,i−nownxtnow]S(i−nxtnow,i] (3) 综合三者得 S [ 1 , n x t n o w ] S ( i − n x t n o w , i ] ( E n d ) S[1,nxt_{now}]S(i-nxt_{now},i]\ \ \ (End) S[1,nxtnow]S(i−nxtnow,i] (End) 即 n x t n o w nxt_{now} nxtnow 也是 S [ 1 , i ] S[1,i] S[1,i] 中的 border 之一。因为 n x t nxt nxt 存的总是当前最长的 border所以 n x t n o w nxt_{now} nxtnow 是 n o w now now 后紧接着的 border不会漏数。所以我们在求 n x t i nxt_i nxti 的时候顺便记录跳的次数 K i ← K n x t i 1 K_{i}\gets K_{nxt_i}1 Ki←Knxti1。对于求得最长且合法的 border先和求 n x t nxt nxt 的过程一样求得最长的匹配成功的 n o w now now然后再继续跳直到 2 × n o w ≤ i 2\times now\le i 2×now≤i此时 n o w now now 即为所求。
Censoring S
提前算出 T T T 的前缀哈希开一个栈逐个往里扔 S i S_i Si 并计算栈中元素的前缀哈希。如果发现栈中元素有 ∣ T ∣ |T| ∣T∣ 个了就判断栈中栈顶 ∣ T ∣ |T| ∣T∣ 个元素的哈希值是否和 T T T 相同如果相同则弹出这 ∣ T ∣ |T| ∣T∣ 个元素。最后从栈底输出到栈顶即可。
SZA-Template
显然合法的印章一定是字符串中的一个 border至少前后缀相同这个印章才可能覆盖整个字符串考虑 dp。设 f i f_{i} fi 表示印 [ 1 , i ] [1,i] [1,i] 这一段所需最短的印章长度。显然 f 1 1 f_11 f11 即用自己作为整个印章。转移时从自己的 border 处转移尝试使用 border 的印章盖出自己但因为 KMP 中求出的 n x t nxt nxt 已经涵盖了较小的 border所以直接从 n x t i nxt_i nxti 处转移即可。转移方程即为 f i ← min ( i , f n x t i ) f_i\gets \min(i,f_{nxt_i}) fi←min(i,fnxti)。
但不是随时都可以转移的。如果想从 n x t i nxt_i nxti 处转移最多最多就是用 n x t i nxt_i nxti 作为印章然后在之前的一段后面加上一段 n x t i nxt_i nxti 印出 i i i 来。即存在一个 j j j使得两者使用的印章长度相同有印章能用且 i − n x t i ≤ j i-nxt_i\le j i−nxti≤j印章一下盖得完。开一个桶记录一下即可注意 1 1 1 一开始就丢进桶里。
String Compression
考虑 dp。设 f i f_i fi 表示前 i i i 个字符进行压缩的最小长度初始化 f i i 1 f_ii1 fii1 即不进行任何内部的压缩。令 c n t i , j cnt_{i,j} cnti,j 表示完全压缩 [ i , j ] [i,j] [i,j] 这一段后的长度显然这一段需要去找 [ i , j ] [i,j] [i,j] 的最短循环节。所以我们按照每个 i i i 为开头都跑一遍 KMP 求 n x t nxt nxt 的过程此时令 i i i 为模式串的开头此时求得的 j − i 1 − n x t j − i 1 j-i1-nxt_{j-i1} j−i1−nxtj−i1 即为循环节需要判断能否整除 j − i 1 j-i1 j−i1不能则说明不存在循环节只能不进行内部压缩。转移方程即为 f i min { f j c n t j 1 , i } ( j i ) f_i\min\{f_jcnt_{j1,i}\}\ (j i) fimin{fjcntj1,i} (ji)。
// 核心代码S 下标从 1 开始。
for (int i 1;i n;i )f[i] i 1;
for (int i 0;i n;i ) { // i 0 时相当于尝试直接整段压缩。KMP(S i); // 跑出以 i 1 为开头的 nxt 数组for (int len 1;i len n;len ) {int j i len;if (len % (len - nxt[len]) 0) f[j] min(f[j],f[i] count(len / (len - nxt[len])) len - nxt[len]);else f[j] min(f[j],f[i] len 1);// count 表示数出十进制位数。}
}说无可说 问题在于如何快速计算两个串的相似度。朴素算法有 dfs动态规划等用 f ( i , j ) f(i,j) f(i,j) 表示 A A A 的前 i i i 个字符与 B B B 的前 j j j 个字符需要多少次才能相同则 f ( i , j ) min { f ( i − 1 , j ) 1 , f ( i , j − 1 ) 1 , f ( i − 1 , j − 1 ) [ A i ≠ B j ] } f(i,j)\min\{f(i-1,j)1,f(i,j-1)1,f(i-1,j-1)[A_i\ne B_j]\} f(i,j)min{f(i−1,j)1,f(i,j−1)1,f(i−1,j−1)[AiBj]} 由于 f ( i , j ) ≥ ∣ i − j ∣ f(i,j)\ge |i-j| f(i,j)≥∣i−j∣考虑到操作次数不超过 8 8 8则第二维可以改成 Δ i \Delta i Δi于是复杂度降为 O ( 16 × ∣ A ∣ ) O(16\times |A|) O(16×∣A∣)用 h ( k , Δ i ) h(k,\Delta i) h(k,Δi) 表示使 f ( i , Δ i ) k f(i,\Delta i)k f(i,Δi)k 的最大的 i i i。因为更大的 i i i 可以更早匹配完中间用 lcp 来转移二分 hash 也能计算 lcp复杂度降为 O ( 64 × log ∣ A ∣ ) O(64\times \log |A|) O(64×log∣A∣)。 这是老师上课讲的做法相信各位都是一知半解反正我不大会就对了所以这里讲一种相对暴力的做法。
首先 O ( n 2 ) O(n^2) O(n2) 枚举两个 A , B A,B A,B考虑暴搜算出 A , B A,B A,B 之间的相似度因为操作次数不会超过 8 8 8 所以边搜边剪枝的复杂度不会太高。全局开一个 a n s ans ans 记录当前算到的最小操作次数最后开一个桶记录对数即可。
课后
大段排骨
很像提高 1 的 第二饭堂。从 S S S 的前后往中间扫边扫边计算此时的前后缀哈希值如果发现相等则说明在这之前的一段可以分出来。不断往中间靠拢即可。最后如果中间还剩下一段或 ∣ S ∣ |S| ∣S∣ 为奇数则答案还要再加一。
倍增计算
首先破环成链。先用前缀和算环上任意一段中 A,B,C \texttt{A,B,C} A,B,C 字母的数量在链上处理然后就可以钦定环的起点按照 4 4 4 的幂次遍历整个环算答案了。
// 核心代码
// sum[i][0/1/2] 表示在链上 i 之前有几个 A/B/C。
for (int i 1, ans 0, pos i;i m;i , ans 0, pos i) {for (int j n - 1;j 0;j --) {for (int k 0;k 3;k )ans (1 (j 1)) - sum[pos (1 (j 1)) * (k 1)][k] sum[pos (1 (j 1)) * k][k];// 修改次数为环上所有字符 - 与目标字符一样的字符数。pos (1 (j 1)) * 3; // 往后处理}res min(res,ans);
}字符游戏
这回想明白辣如果字符串字典序单调不减那么显然删除的字符越靠后原串字典序越小或相等而如果原串有一个部分字典序减小了假设 S i S i 1 S_iS_{i1} SiSi1则 i i i 越靠前删掉 S i S_i Si 所得的字典序越小且一定不大于其他诸如 S j ≤ S j 1 S_j\le S_{j1} Sj≤Sj1 时删掉 S j S_j Sj 后的字典序。
我们以 S aabbaaaba S\texttt{aabbaaaba} Saabbaaaba 为例。一眼盯真可得删除 S 4 S_4 S4 时字典序最小因为 S 4 S 5 S_4S_5 S4S5进一步可以发现一段连续且相同的字符中删掉任意一个所得字典序都是不变的所以删除 S 3 S_3 S3 后字典序也是最小的。根据一开始的结论删掉 S 8 S_8 S8 后所得字典序就是次小的。为了方便我们将这么得到的字符串称为一类字符串。剩下的情况中 S [ 1 , 2 ] , S [ 5 , 8 ] S[1,2],S[5,8] S[1,2],S[5,8] 中分别任意选择一个字符删去分别得到的字典序不变。仍根据一开始的结论得到的字典序从小至大为删去 S 9 S_9 S9、删去 S [ 5 , 8 ] S[5,8] S[5,8]、删去 S [ 1 , 2 ] S[1,2] S[1,2]。我们将这么得到的字符串称为二类字符串。一类字符串的字典序总是小于二类字符串。
于是做法就出来了开一个栈记录此时的字符从前往后枚举 i i i如果 S i S_i Si 的字典序大于栈顶则说明删去栈顶后会得到一个一类字符串字典序大于之前得到的一类字符串。然后把栈顶部与栈顶字符相同的部分都弹出反着输出。记得在栈中塞回一个分界符区分二类字符串。
然后就考虑栈中剩下的二类字符串处理方法与之前类似从栈顶往下一路考虑即可。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/85080.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!