后缀数组 - Suffix Array

【Suffix Array】后缀数组详解 后缀数组,一种处理字符串的有力工具。 后缀:假设字符串的长度为n,那么后缀 i 表示 从 i 到 n 这一段字符串。ababa:后缀 3 为 aba。 后缀数组就是对字符串的所有后缀排序。 很显然,我们可以通过快速排序在的时间复杂度内对 n 个后缀进行排序,但是字符串之间的比较不是的,而是的,所以整体时间复杂度为。 Manber和Myers发明的倍增算法,它的时间复杂度为。 下面详细介绍... 阅读详情

记得 VFK 曾经说过:“NOI试机的时候,有很多选手都在照着书打后缀数组,因为不理解。”。
的确,后缀数组真的很容易忘记啊,明明以前搞得很清楚,过一阵子又忘记了,一忘记,又容易写错,一写错,考场上就没有办法了。所以简单的写一下核心内容,方便用时回忆。

简介

KMP 字符串匹配算法,它可以在 O(m + n)O(m+n) 的时间内完成对模版串的匹配。然而,对于多模版串的匹配,每一次都要预处理,十分不高效。这时有 AC 自动机的出现,虽然可以多模匹配,但每次匹配仍然要遍历整个字符串,对于很长很长的字符串,例如 DNA 信息,有上亿的数据,这时候每一次查询遍历就不太科学了。这时就要预处理文本串。
假设文本串为 banana,可以在其末尾加一个字符 $(代表一个没在串中出现过的字符),稍后解释原因。把其所有后缀(banana$, anana$, nana$, ana$, na$, a$, $),放进一棵 Trie 树。

有了这个后缀 Trie,查找一个长度为 mm 的模版时只需要进行一次普通的 Trie 查找,时间复杂度为 O(m)O(m),如上图所示。
而在实际中,我们会把后缀 Trie 中没有分支的链合并在一起,得到所谓的后缀树(Suffix Tree),然而构造起来太麻烦,复杂难懂,而且容易写错,而它的后缀数组则是一个不错的替代品,它时间效率高,而且代码简单,不易写错。空间大概是后缀树的 \frac{1}{5}​5​​1​​ ~ \frac{1}{3}​3​​1​​。
或许你们已经注意到了,在绘制上面的后缀 Trie 的时候,我故意把每个节点的所有子节点排好序,字典序小的在左边(规定 $ 最小),然后在叶子节点上标上了后缀首字母的下标。例如后缀 ana$ 所对应的叶子节点标有 33,为了叙述方便,我们把以下标 kk 开头的后缀(包含$),叫做后缀 kk,记作 。
我们从左到右排列出所有叶子的编号,就可以得到后缀数组 (Suffix Array),banana 的后缀数组为 ,它就是所有后缀按照字典序从小到大排序后的结果。

用后缀数组求解匹配

有了这个后缀数组,我们就可以通过二分搜索(利用后缀数组字典序的单调性)的方法,来在 O(m\log n)O(mlogn) 的时间内完成匹配。

int m, sa[maxn];
int cmp_suffix(const char *pattern, int p) {
    return strncmp(pattern, str + sa[p], m);
}
//在 Suffix Array 数组的(lb, ub) 区间寻找字符串 pattern
void find(const char* pattern, int lb, int ub) {
         m = strlen(pattern);
        if(cmp_suffix(pattern, lb + 1) < 0) return; //如果当前最小的字典序都大于模版串的字典序,肯定无法匹配
        if(cmp_suffix(pattern, ub - 1) > 0) return; //如果当前最大的字典序都小于模版串的字典序,肯定无法匹配
        while(ub - lb > 1) {
            int mid = (lb + ub) / 2;
            int res = cmp_suffix(pattern, mid);
            if(!res) {
                printf("Match! Index:%d\n", sa[mid]);
                find(pattern, lb, mid); //(lb, mid) 可能还有匹配
                find(pattern, mid, ub); //(mid, ub) 可能还有匹配
                return;
            }
            if(res < 0) ub = mid; //如果模版串的字典序比后缀 sa[mid] 小,那么解的范围变为 (lb, mid) 
            else lb = mid;    //如果模版串的字典序比后缀 sa[mid] 小,那么解的范围变为 (mid, ub) 
        }
}

strncmp(s1, s2, size) 的意思是比较是比较 s_1, s_2s​1​​,s​2​​ 的前 size 个字符,如果相同返回 0,如果不同,则返回第一个不同位置 kk的 s_1[k] - s_2[k]s​1​​[k]−s​2​​[k]。

计算 Suffix Array

提到  数组,就不得不说一说  数组。\mathrm{SA}[i]SA[i] 表示的是字典序排名为 ii 的后缀是谁(字典序越小的排名越靠前)。而 \mathrm{rank}[i]rank[i] 表示的是后缀 ii 所对应的排名是多少。事实上,它们是反函数的关系,即 \mathrm{SA}[\mathrm{rank}[i]] = iSA[rank[i]]=i。
特别注意,字符串的长度是原串的长度 +1,因为有 $ 这个字符,而在实现的时候我们就用 0 来表示 $,显然这个后缀 $ 排名最靠前。

朴素的排序

那么如何计算  数组呢?根据定义,我们可以通过一次快速排序得到后缀数组,比较次数 O(n\log n)O(nlogn),单次比较时间 O(n)O(n),总体 O(n^2\log n)O(n​2​​logn),时间复杂度太高,难以承受。
不用担心后缀长度不一致的比较问题,因为有 $ 字典序最小,所以总能在不越界的情况下完成比较。
不过这方法几乎不用,非常慢,但是在没有时间的时候还是可以 rush 一发拿点暴力分,毕竟可以音速实现。

倍增思想

下面介绍一种 Manber 和 Myers 发明的倍增算法,它的时间复杂度是 O(n\log n)O(nlogn)。

static const int maxNode = 100000 + 3;
int sa[maxNode], rank[maxNode], n;
char str[maxNode];
void buildSA(int m) {
    static int cnt[maxNode], tmpSA[maxNode], rank1[maxNode], rank2[maxNode];
    n = strlen(str) + 1; str[n] = 0; //结尾的 $
    fill(cnt, cnt + m, 0);
    for (int i = 0; i < n; ++i) cnt[(int)str[i]]++;
    for (int i = 1; i < m; ++i) cnt[i] += cnt[i - 1];
    for (int i = 0; i < n; ++i) rank[i] = cnt[(int)str[i]] - 1;
    for (int l = 1; l < n; l <<= 1) {
        for (int i = 0; i < n; ++i)
            rank1[i] = rank[i], rank2[i] = i + l < n ? rank1[i + l] : 0;
        fill(cnt, cnt + n, 0);
        for (int i = 0; i < n; ++i) cnt[rank2[i]]++;
        for (int i = 1; i < n; ++i) cnt[i] += cnt[i - 1];
        for (int i = n - 1; i >= 0; --i) tmpSA[--cnt[rank2[i]]] = i;
        fill(cnt, cnt + n, 0);
        for (int i = 0; i < n; ++i) cnt[rank1[i]]++;
        for (int i = 1; i < n; ++i) cnt[i] += cnt[i - 1];
        for (int i = n - 1; i >= 0; --i) sa[--cnt[rank1[tmpSA[i]]]] = tmpSA[i];
        bool unique = true;
        rank[sa[0]] = 0;
        for (int i = 1; i < n; ++i) {
            rank[sa[i]] = rank[sa[i - 1]];
            if (rank1[sa[i]] == rank1[sa[i - 1]] && rank2[sa[i]] == rank2[sa[i - 1]]) unique = false;
            else rank[sa[i]]++;
        }
        if (unique) break;
    }
}

具体我不细讲,因为这文章只是用来回顾的。
一开始直接利用字符来计算初始的 ,接着开始倍增,以  为第一关键字,也就是对于一个后缀的起点,前 ll 个字符的排名, 为第二关键字,也就是后 ll 个字符的排名,特别注意一旦越界,直接给 00。
如何体现第二关键字的作用呢?先计算出 ,字典序越是小的越排在  的前面,用这句话巧妙地实现(注意倒序循环):
for (int i = n - 1; i >= 0; --i) tmpSA[--cnt[rank2[i]]] = i;
再根据第一关键字排序的时候,就利用  的顺序来排序,我们也是要字典序越是小的越排在  的前面,同样这样实现:
for (int i = n - 1; i >= 0; --i) sa[--cnt[rank1[tmpSA[i]]]] = tmpSA[i];
由于  是大到小的,同一个位置越先访问,领取到的排名越靠后。为了保证相同的  下,越靠前的后缀越小,所以我们倒序循环,这也是两次倒序循环的原因。
后面没什么好说的了,注意在  互不相同的时候跳出是一个不错的优化。

DC3 什么的,还是算了吧。巨大的常数。

计算 Height

高度数组: 是一个一维数组,保存了在后缀数组中相邻两个后缀的最长公共前缀(Longest Common Prefix,LCP)长度。

即  表示存在的最大的 xx,满足对于任何 k\in [0,x)k∈[0,x),有 。

除了朴素比较外,可以 O(n)O(n) 求解,代码如下:

void getHeight() {
    int k = 0;
    for (int i = 0; i < n - 1; ++i) {
        if (k) --k;
        int j = sa[rank[i] - 1];
        while (str[i + k] == str[j + k]) k++;
        height[rank[i]] = k;
    }
}

原理是按照 ,也就根据原串的顺序计算。整个求解的核心是这样一个不等式:如果设 h[i] = \mathrm{height}[\mathrm{rank}[i]]h[i]=height[rank[i]] 的话,一定有 h[i] \ge h[i - 1] - 1h[i]≥h[i−1]−1,下面来证明一下:
如果设 \mathrm{Suffix(i - 1)}Suffix(i−1) 的在后缀数组中的前一个是 \mathrm{Suffix(\mathrm{SA}[\mathrm{rank}[i - 1] - 1])} = \mathrm{Suffix(k)}Suffix(SA[rank[i−1]−1])=Suffix(k),那么我们已经求出它们的最长公共前缀是 h[i - 1]h[i−1],考虑去掉它们的头一位,得到前缀 \mathrm{Suffix(i)}Suffix(i) 与 \mathrm{Suffix(k + 1)}Suffix(k+1),显然它们的最长公共前缀 h[i - 1] - 1h[i−1]−1,而 \mathrm{Suffix(i)}Suffix(i) 的在后缀数组中的前一个是 \mathrm{Suffix(\mathrm{SA}[\mathrm{rank}[i] - 1])} = \mathrm{Suffix(p)}Suffix(SA[rank[i]−1])=Suffix(p),是与 \mathrm{Suffix}(i)Suffix(i) 字典序最为接近的一个(因为 \mathrm{Suffix(k + 1)}Suffix(k+1) 在  中必定比 \mathrm{Suffix(i)}Suffix(i) 靠前,而 \mathrm{Suffix(p)}Suffix(p) 就是 \mathrm{Suffix(i)}Suffix(i) 之前一个),\mathrm{Suffix(i)}Suffix(i) 与 \mathrm{Suffix(k + 1)}Suffix(k+1) 的最长公共前缀不可能大于 \mathrm{Suffix(i)}Suffix(i) 与 \mathrm{Suffix(p)}Suffix(p) 的最长公共前缀的,所以它们的最长公共前缀至少是 h[i] \ge h[i - 1] - 1h[i]≥h[i−1]−1。
这样做,就不用从头比较了。

求解 LCP

依照刚刚的理论,对于 \mathrm{Suffix}(i)Suffix(i),只有可能与其在  中相邻的元素有最长的最长公共前缀,如果要求 \mathrm{Suffix}(i)Suffix(i) 与 \mathrm{Suffix}(j)Suffix(j) 的最长公共前缀,且 \mathrm{rank}[i] < \mathrm{rank}[j]rank[i]<rank[j],那么 \mathrm{LCP}(\mathrm{Suffix}(i), \mathrm{Suffix}(j)) = \min \mathrm{height}[k], k \in[\mathrm{rank}[i] + 1, \mathrm{rank}[j]]LCP(Suffix(i),Suffix(j))=minheight[k],k∈[rank[i]+1,rank[j]]。这是一环扣一环的过程。
问题转化为区间最值查询(Range Minimum/Maximum Query,RMQ)问题,可以使用稀疏表(Sparse Table,ST)算法解决。该算法在  的时间内预处理,并可在 O(1)O(1) 的时间内完成每个询问。

还有单次匹配 O(m + \log n)O(m+logn) 的算法以及一些常用技巧,等我碰到了题再补吧~
鸣谢 Menci 的 SuffixArray 讲解,部分懒得打直接搬过来了。

 

来源:https://blog.sengxian.com/algorithms/suffix-array

看动画学算法系列之:后缀数组suffix array 在之前的文章中,我们讲到了后缀树和它的一些特性。后缀树主要用来做模式匹配中,比如全文索引,寻找公共前缀等,非常的有用。同样的后缀数组和后缀树的作用非常类似,和后缀树相比,后缀数组更简单并且更加节省空间,今天我们将会详细介绍下后缀数组的特性和使用。 阅读详情

相关推荐

后缀数组(Suffix Array)

后缀数组(Suffix Array)标程。采用DA算法。RMQ预处理询问。

后缀数组(Suffix Array )

定义说明: 后缀:从字符串的任意一个字符开始一直到最后一个字符所构成的一个字符串 例如:对于字符串"abcdefg","cdefg"、"efg"、"g"都是后缀,"cde"则不是后缀  在学习后缀数组之前,我们首先要知道后缀数组是什么? 后缀数组:将一个字符串的所有后缀按照字典序从小到大进行排序得到的一个数组 给定字符串str,长度为n,位置编号从1到n Suffix数组(string...

AcTarjan的博客 2722

SuffixArray 扩展(以单词为单位) 源码

SuffixArray 扩展(以单词为单位) 源码

后缀数组(suffix array)详解

写在前面 在字符串处理当中,后缀树和后缀数组都是非常有力的工具。 其中后缀树大家了解得比较多,关于后缀数组则很少见于国内的资料。 其实后缀数组是后缀树的一个非常精巧的替代品,它比后缀树容易编程实现, 能够实现后缀树的很多功能而时间复杂度也不太逊色,并且,它比后缀树所占用的空间小很多。 可以说,在信息学竞赛中后缀数组比后缀树要更为实用! 因此在本文中笔者想介绍一下后缀数组的基本概念、构造...

weixin_34198762的博客 1022

VCSA 上的 vmware-vpxd 服务失败,Web端提示503 服务不可用 (503 Service Unavailable)

昨晚VCSA碰到一个很诡异的问题: 使用 vSphere Web Client 连接到 vCenter Server Appliance时失败,提示: 503 服务不可用 (503 Service Unavailable) 无法连接端点 (Failed to connect to endpoint):[N7Vmacore4Http20NamedPipeServiceSpecE:0x00007f975808ee30] _serverNamespace = / action = Allow _pipeNam

徐华的博客 2万+

基于Matlab-Simulink的大林算法控制系统设计与振铃消除实验

本文详细介绍了基于Matlab-Simulink的大林算法控制系统设计与振铃消除实验。通过分析纯滞后系统的控制难题,探讨大林算法在工业控制中的应用优势,并提供Matlab-Simulink环境搭建、控制器D(z)设计、振铃消除及仿真实验的完整流程。文章还对比了大林算法与PID控制的性能差异,为工程实践提供参考。

dropout6artist的博客 798

后缀数组(Suffix Array)在字符串匹配中的应用

前言 首先抛出一个问题: 给定300w字符串A, 之后给定80w字符串B, 需要求出 B中的每一个字符串, 是否是A中某一个字符串的子串. 也就是拿到80w个bool值. 当然, 直观的看上去, 有一个暴力的解法, 那就是 双重循环, 再调用字符串德contains方法, 想法很美好, 现实很残酷. 如果你真的这么实现了(是的, 我做了.), 就会发现,效率低到无法接受.具体的效率测评在后文给出...

呼延十 1288

瑞萨RA6M5驱动ST7735屏幕避坑指南:从软件SPI到显示汉字与图形的完整流程

本文详细介绍了瑞萨RA6M5单片机驱动ST7735屏幕的完整流程,涵盖硬件连接、软件SPI控制、汉字显示及图形绘制优化等关键步骤。通过避坑指南和实用代码示例,帮助开发者解决屏幕初始化、时序控制、字库移植等常见问题,实现高效稳定的显示效果。

weixin_28705087的博客 275

字符串算法之后缀数组(Suffix Array)详细解读

后缀数组广泛应用于文本处理、基因序列分析、自然语言处理等领域。它在字符串查找、文本比较、模式匹配和数据压缩等任务中具有重要的应用价值。

欢迎来到我的博客!这里是一个专注于计算机技术的分享平台,涵盖编程开发、算法研究、系统架构、软件工程等多个领域。无论你是初学者还是资深开发者,都能在这里找到有价值的内容。 1042

合并数组与非合并数组——SystemVerilog

合并型数组(packed): — 合并型数组可以实现连续的存储,赋值时不需要用 '{ }。 //合并型数组 bit [3] [7:0] b_pack // 表示3行 x 8列的数组,[3]是高维度(高维度),[7:0]是低维度(第一维) 数组中,数据排列为{b_pack[3],b_pack[2],b_pack[1],b_pack[0]},其中每个b_pack为8个bit; bit是二值逻辑,每位bit只占据1位。故24位bit只占据一个word(一般一个word为32bit)的存储空间

SummerXRT的博客 7639

电磁兼容标准体系解析:从基础到认证的完整指南

本文系统解析了电磁兼容(EMC)标准体系,从基础概念到认证流程提供完整指南。文章阐述了EMC对于产品可靠性与市场准入的重要性,详细拆解了基础标准、通用标准、产品类标准及认证流程四层结构,并结合5G设备与工业控制等实战案例,为工程师提供了从设计初期到认证测试的高效应用策略。

weixin_29294975的博客 355

后缀数组(Suffix Array)

啥是后缀数组?难不难? 后缀数组看上去是一个很高深,很玄妙的东西。 但首先,我们要树立一个观点:它!不难!! 实际上,他也真的不难。难的只是其中一个想法的操作。除此以外,不难。 后缀数组的概念 首先,我们需要了解一下什么是后缀 后缀,顾名思义,缀在后面的东西。在字符串中,就是缀在字符串后面的东西,大家可以联系一下英语单词中的后缀来理解。 举个例子 字符串:abaab 后缀分别有: abaab, ...

一个小渣渣的博客:) 1520

激光可燃气体传感器:核心技术环节分析

激光可燃气体传感器核心技术涵盖七个维度:1)TDLAS光谱吸收与调制技术(检测下限ppm级);2)DFB激光器与双闭环温控系统(波长稳定性±0.0036nm);3)微弱信号处理技术(信噪比提升60dB);4)环境补偿算法(温压补偿误差<±1%);5)本质安全防爆设计(ExiaIICT4Ga等级);6)智能边缘计算(STM32L476芯片);7)多重性能验证(响应时间<1秒)。通过微型化、多气体检测等创新,该技术已在工业安全等领域替代传统方案,实现高精度检测。(149字)

上市公司前技术负责人,20 年软硬件深耕 + 百人研发团队管理经验,名校理工科背景加持。从技术攻坚到团队掌舵,沉淀海量项目实战与管理心得,专注分享软硬件干货、疑难问题解决方案,既是靠谱知识传播者,也盼链接优质项目共成长。 1045

[置顶]后缀数组(suffix array)详解

写在前面 在字符串处理当中,后缀树和后缀数组都是非常有力的工具。 其中后缀树大家了解得比较多,关于后缀数组则很少见于国内的资料。 其实后缀数组是后缀树的一个非常精巧的替代品,它比后缀树容易编程实现, 能够实现后缀树的很多功能而时间复杂度也不太逊色,并且,它比后缀树所占用的空间小很多。 可以说,在信息学竞赛中后缀数组比后缀树要更为实用! 因此在本文中笔者想介绍一下后缀数组的基本概念、构造

北岛知寒 6751

别再只看成交量了!用通达信这个VOL监测公式,一眼识别主力吸筹与出货信号

本文详细介绍了通达信VOL监测公式的实战应用,通过彩色量柱和换手率分层技术,帮助投资者识别主力吸筹与出货信号。该公式结合动态量能标准化和多周期均线组合,提供可视化主力行为图谱,提升交易决策的准确性。

weixin_28693447的博客 662

11、基于多阵列电极的表面肌电信号运动估计

本文介绍了一种基于多阵列电极的表面肌电信号运动估计技术,旨在实现对手腕和手指运动的高精度估计,解决传统人机接口在操控多自由度机器人时的局限性。通过多阵列电极系统采集前臂肌肉活动信号,结合独立成分分析(ICA)和非负矩阵分解(NMF)等信号处理方法,提取肌肉协同作用,进而生成运动指令以控制机器人或假肢手。文章对比了多阵列电极与传统电极的优势,展示了运动估计算法的整体流程,并以假肢手控制为例说明其实际应用。最后探讨了该技术在测量精度、应用拓展、智能化和便携化等方面的未来发展方向。该技术为假肢控制、康复训练和人机

ik678901234的博客 87

后缀数组 (Suffix Array) 与 后缀树 (Suffix Tree)

后缀数组和后缀树都是。

solomonzw的博客 1123

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

Suffix Array【后缀数组】

算法介绍 后缀数组是用来解决⼀类字符串问题的⼯具 算法目标是计算出字符串的每个后缀的排名 几个要用到的定义 n : 字符串的长度 sz : 字符集 s :字符串,方便起见,我们从0开始标号,即 s[0]~s[n-1] suf[i] : 下标从i开始的后缀 rk[i] : 以 s[i] 开头的后缀的排名为rk[i] sa[i] :...

Shelter 694

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

【SPOJ-SARRAY】Suffix Array【后缀数组】

题意: 求一个字符串的SA数组。 回顾一下模板。 出题人说O(n)的才可以得100分,然而倍增也过了。 #include #include #include using namespace std; const int maxn = 100005, M = 70; int num[maxn], sa[maxn]; int wa[maxn], wb[maxn],

BraketBN 520

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

后缀数组suffix array

自用模板: sa:字典序中排第i位的起使位置在str中第sa[i],sa[1,n]rk:str第i个位置的后缀在字典序中排第几,rk[1,n]height:字典序排i和i-1的后缀的最长公共前缀,height[2,n] #include<bits/stdc++.h> #define maxn 1000050 using namespace std; char...

atk006680的博客 149

后缀数组 (Suffix Array)

后缀数组是处理字符串匹配、去重、搜索等高级问题的利器,主要应用于: 大模型数据清洗:精确去重和防污染 DNA序列比对 代码克隆检测和子串检索 数据压缩:如bzip2算法 核心组件包括: SA数组:存储排序后的后缀索引 Rank数组:记录每个后缀的排名 Height数组:计算最长公共前缀(LCP) 关键应用场景: 模式匹配(O(MlogN)) 最长重复子串(Height数组最大值) 本质不同子串统计 构建方法采用倍增法(O(NlogN)),配合Kasai算法(O(N))计算Height数组。

lifallen的博客 728

Suffix Array 后缀数组

后缀数组 顾名思义,SuffixArray(以下有时简称SA) 和字符串的后缀有关。 后缀:字符串中某个位置一直到结尾的子串。(SA中讨论包括了原串和空串),所以共有len+1个后缀。 后缀数组: 字符串的所有后缀组成的按字典序从小到大排好的数组。由于SA中记录的都是字符串的后缀,所以SA只需要记录其表示的后缀的起始位置。 由于比较字典序是O(n)的,所以暴力算法的复杂度将

xuelanghu407 3289

Suffix Tree (后缀树)、Suffix Array (后缀数组)、LZW树详细解读

以下是 后缀树(Suffix Tree)、后缀数组(Suffix Array) 以及 LZW树 的详细介绍,包括定义、原理、优缺点和应用场景。

欢迎来到我的博客!这里是一个专注于计算机技术的分享平台,涵盖编程开发、算法研究、系统架构、软件工程等多个领域。无论你是初学者还是资深开发者,都能在这里找到有价值的内容。 1732
上一篇: 第七章 递归、DFS、剪枝、回溯等问题 ------------- 7.4 硬币表示某个给定数值
下一篇: DFS(深度优先搜索)
Estrellas_
博客等级 码龄9年 273粉丝 233原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值