📌  相关文章
📜  用于查询给定字符串在恒定时间内的旋转和第 K 个字符的 C++ 程序(1)

📅  最后修改于: 2023-12-03 15:40:54.466000             🧑  作者: Mango

用于查询给定字符串在恒定时间内的旋转和第 K 个字符的 C++ 程序

本文将介绍如何在 C++ 中实现查询给定字符串的旋转和第 K 个字符。我们将使用一些编程技巧和数据结构来实现此功能。

查询给定字符串的旋转

给定字符串的旋转是指将字符串旋转任意次后得到的字符串。例如,将字符串 "abcd" 旋转一次得到字符串 "dabc",旋转两次得到字符串 "cdab",旋转三次得到字符串 "bcda",以此类推。

为了查询给定字符串的旋转,我们可以使用 KMP 算法的思想。首先,通过在原字符串两端添加一个特殊标记,我们可以构建出一个循环字符串。例如,将字符串 "abcd" 转换为 "#abcd#"。接着,我们可以使用 KMP 算法,在循环字符串中查找原字符串的所有旋转,并找到其首字母在循环字符串中的位置。最后,我们可以根据首字母的位置和字符串的长度来确定字符串的旋转是多少次。

下面是查询字符串旋转的 C++ 代码,其中 str 表示原始字符串,rot 表示字符串的旋转,pos 表示字符串的首字母在循环字符串中的位置:

#include <iostream>
#include <cstring>
using namespace std;

const int MAXN = 1000005;

int fail[MAXN];
char str[MAXN], s[MAXN];

void get_fail(char* p)
{
    int m = strlen(p);
    fail[0] = fail[1] = 0;
    for (int i = 1; i < m; i++)
    {
        int j = fail[i];
        while (j && p[i] != p[j]) j = fail[j];
        fail[i + 1] = p[i] == p[j] ? j + 1 : 0;
    }
}

void get_rot(char* str, int len, char* s, int& pos, int& rot)
{
    // 构造循环字符串
    int m = len * 2;
    for (int i = 0; i < len; i++)
    {
        s[i] = s[i + len] = str[i];
    }
    s[m] = '\0';

    // 计算 KMP 失配数组
    get_fail(str);
    get_fail(s);

    // 在循环字符串中查找原字符串的所有旋转
    int j = 0;
    for (int i = 0; i < m; i++)
    {
        while (j && s[i] != str[j]) j = fail[j];
        if (s[i] == str[j]) j++;
        if (j == len)
        {
            pos = i - len + 1;
            rot = pos % len;
            break;
        }
    }
}

int main()
{
    cin >> str;

    int len = strlen(str);
    int pos, rot;
    get_rot(str, len, s, pos, rot);

    cout << "The string " << str << " is rotated " << rot << " times." << endl;
    return 0;
}

以上代码中,get_fail 函数用于计算 KMP 失配数组,get_rot 函数用于查找字符串的旋转,主函数用于读取输入,调用查询函数并输出结果。

查询给定字符串的第 K 个字符

查询给定字符串的第 K 个字符是指在字符串中找到第 K 个字符。为了实现这个功能,我们可以使用线段树和二分查找来实现。具体来说,我们可以将字符串表示为一棵线段树,其中每个节点表示一个子串。我们还需要在每个节点上维护一个计数数组,该数组记录该子串中每个字符出现的次数。如此,我们可以通过二分查找在树上遍历,找到第 K 个字符所在的子串,并根据计数数组得到该字符的值。

下面是查询字符串第 K 个字符的 C++ 代码,其中 str 表示原始字符串,N 表示字符串的长度,pos 表示根节点所代表的子串在原始字符串中的位置,cnt 表示该子串中每个字符出现的次数,ch 表示第 K 个字符的值:

#include <iostream>
#include <cstring>
using namespace std;

const int MAXN = 1000005;

int lc[MAXN * 4], rc[MAXN * 4], cnt[MAXN * 4][26], pos[MAXN * 4];
char str[MAXN];

void build(int p, int l, int r)
{
    if (l == r)
    {
        pos[p] = l;
        for (int i = 0; i < 26; i++)
            cnt[p][i] = str[l - 1] == 'a' + i ? 1 : 0;
        return;
    }
    int mid = (l + r) >> 1;
    lc[p] = p << 1, rc[p] = lc[p] | 1;
    build(lc[p], l, mid);
    build(rc[p], mid + 1, r);
    pos[p] = pos[lc[p]];
    for (int i = 0; i < 26; i++)
        cnt[p][i] = cnt[lc[p]][i] + cnt[rc[p]][i];
}

int query(int p, int l, int r, int k)
{
    if (l == r) return pos[p];
    int mid = (l + r) >> 1;
    if (k <= cnt[lc[p]][0]) return query(lc[p], l, mid, k);
    else return query(rc[p], mid + 1, r, k - cnt[lc[p]][0]);
}

int main()
{
    cin >> str;
    int N = strlen(str);
    build(1, 1, N);

    int K;
    while (cin >> K)
    {
        int kth = query(1, 1, N, K);
        int ch = str[kth - 1];
        cout << "The " << K << "th character of " << str << " is " << (char) ch << "." << endl;
    }

    return 0;
}

以上代码中,build 函数用于构建字符串的线段树,query 函数用于在树上进行二分查找,主函数用于读取输入,调用查询函数并输出结果。

结论

在本文中,我们介绍了如何在 C++ 中实现查询给定字符串的旋转和第 K 个字符。通过使用 KMP 算法和线段树等数据结构,可以实现对字符串的有效查询。在实际应用中,这些算法和数据结构可以极大地提高效率,减少计算时间和空间开销。