UVa 632 Compression (II)

题目描述

本题要求实现 Burrows–Wheeler\texttt{Burrows–Wheeler}BurrowsWheeler 变换BWT\texttt{BWT}BWT)。给定一个长度为 NNN 的字符串 SSS,首先生成它的 NNN 个循环移位字符串 S0,S1,…,SN−1S_0, S_1, \dots, S_{N-1}S0,S1,,SN1,其中 S0=SS_0 = SS0=SSkS_kSkSk−1S_{k-1}Sk1 将第一个字符移动到末尾得到。然后将这 NNN 个字符串按字典序排序,输出排序后矩阵的最后一列所组成的字符串,以及 S1S_1S1(即原串左移一位)在排序后的行号(从 000 开始)。

输入中字符串可能跨多行,每行(除最后一行外)恰好 505050 个字符。输出时,最后一列字符串也按每行 505050 个字符(最后一行可少于 505050)输出。

输入格式

第一行为一个整数 MMM,表示数据集的个数。随后可能有一个空行。每个数据集的第一行为整数 NNNN<1997N < 1997N<1997),表示字符串长度。接下来若干行包含该字符串,每行(除最后一行外)恰好 505050 个字符。数据集之间可能有一个空行。

输出格式

对于每个数据集,输出两行(或更多):

  • 第一行输出 S1S_1S1 在排序后数组中的行号(从 000 开始计数)。
  • 随后若干行输出变换后的最后一列字符串,每行最多 505050 个字符。

每组输出之间用一个空行分隔。

样例

输入

1

6
pascal

输出

1
cpsala

题目分析

Burrows–Wheeler\texttt{Burrows–Wheeler}BurrowsWheeler 变换是数据压缩中的经典预处理步骤,它通过重排字符使得具有相同上下文的字符聚集,便于后续压缩。本题要求实现最基本的变换过程:

  1. 构造所有循环移位(共 NNN 个)。
  2. 按字典序排序这些移位。
  3. 提取排序后每个字符串的最后一个字符,拼接成新字符串。
  4. 输出原串 S1S_1S1(即原串左移一位)所在的行号。

由于 NNN 最大仅为 199719971997,直接生成所有移位并排序是完全可行的。字符串的存储和比较均为 O(N)O(N)O(N),总复杂度约为 O(N2log⁡N)O(N^2 \log N)O(N2logN),在给定数据范围内可以接受。

解题思路

读取字符串

由于输入中字符串可能被分割成多行,每行(除最后一行外)恰好 505050 个字符,我们需要连续读取行,直至累积长度达到 NNN,然后将多余字符丢弃(但题目保证不会多出)。

生成循环移位

设原串为 word,长度为 NNN。初始化一个向量 words,每个元素包含一个整数 label(表示该串是由原串循环左移 label 位得到)和一个字符串 s(当前的循环移位)。

  • 000 个循环移位即为 word
  • 对于 iii111N−1N-1N1,将当前串的第一个字符移到末尾,得到下一个移位,并存入向量。

排序

使用 stable_sort\texttt{stable\_sort}stable_sort 按字符串字典序升序排序。由于 label 可以唯一标识原串的偏移,即使字符串相同(理论上可能重复)也不会影响排序稳定性,但这里使用稳定排序是为了保持原始顺序(实际上并不影响最终结果)。

提取最后一列和行号

遍历排序后的向量 words

  • 将每个字符串的最后一个字符(s.back())拼接到结果字符串 created 中。
  • 若当前 label == 1,则记录当前下标 indexer(即为 S1S_1S1 所在行号)。

输出

先输出 indexer,然后按每行 505050 个字符输出 created

复杂度分析

  • 生成 NNN 个循环移位,每个字符串长度为 NNN,总时间复杂度为 O(N2)O(N^2)O(N2)
  • 排序比较字符串 O(Nlog⁡N)O(N \log N)O(NlogN) 次,每次比较最坏 O(N)O(N)O(N),故排序部分 O(N2log⁡N)O(N^2 \log N)O(N2logN)
  • 总体时间复杂度为 O(N2log⁡N)O(N^2 \log N)O(N2logN),当 N=1997N = 1997N=1997 时约为 4×106×11≈4.4×1074 \times 10^6 \times 11 \approx 4.4 \times 10^74×106×114.4×107 次字符比较,在可接受范围内。
  • 空间复杂度为 O(N2)O(N^2)O(N2),用于存储所有循环移位字符串。

代码实现

// Compression (II)
// UVa ID: 632
// Verdict: Accepted
// Submission Date: 2016-08-16
// UVa Run Time: 0.000s
//
// 版权所有(C)2016,邱秋。metaphysis # yeah dot net

#include <bits/stdc++.h>
using namespace std;

struct cell {
    int label;
    string s;
    bool operator<(const cell& another) const {
        return s < another.s;
    }
};

int main() {
    cin.tie(0); cout.tie(0); ios::sync_with_stdio(false);
    int datasets;
    cin >> datasets;
    string line;
    for (int d = 1; d <= datasets; d++) {
        if (d > 1) cout << '\n';
        int length;
        cin >> length;
        cin.ignore(1024, '\n');
        string word;
        while (true) {
            getline(cin, line);
            word += line;
            if (word.length() >= length) break;
        }
        string s0 = word, s1 = word;
        s1.push_back(s1.front());
        s1.erase(s1.begin());
        vector<cell> words;
        for (int i = 0; i < length; i++) {
            words.push_back((cell){i, word});
            word.push_back(word.front());
            word.erase(word.begin());
        }
        stable_sort(words.begin(), words.end());
        string created;
        int indexer = -1;
        for (int i = 0; i < length; i++) {
            created += words[i].s.back();
            if (indexer == -1 && words[i].label == 1) indexer = i;
        }
        cout << indexer << '\n';
        while (created.length() > 50) {
            cout << created.substr(0, 50) << '\n';
            created = created.substr(50);
        }
        if (created.length() > 0) cout << created << '\n';
    }
    return 0;
}

总结

本题通过直接构造所有循环移位并排序,实现了 Burrows–Wheeler\texttt{Burrows–Wheeler}BurrowsWheeler 变换。核心步骤简洁明了,适用于 NNN 较小的情况。实际应用中,BWT\texttt{BWT}BWT 通常配合其他压缩算法(如游程编码、哈夫曼编码等)使用。本题作为算法实现练习,重点在于掌握循环移位的生成、字典序排序以及索引记录。注意输入跨行处理和输出格式控制。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值