2019.01.26 codeforces 528D. Fuzzy Search(fft)

2019.01.26 Codeforces 528D. Fuzzy Search (FFT)

题目概述

本题的题意是给出一个长度为 $n$ 的文本串 $s$,一个长度为 $m$ 的模式串 $t$,以及允许 $k$ 次错误匹配的限制,求模式串在文本串中的出现次数。其中,错误匹配指的是允许在 $t$ 中最多更改 $k$ 个字符(包括删减和增加)以达到与文本串 $s$ 的匹配。

解题思路

本题的数据范围比较小,可以通过暴力枚举的方法解决,时间复杂度为 $O(nm^2k^2)$,但是很容易超时。

因此,可以考虑使用一些高级算法来提升效率。例如,本题可以使用字符串编码算法,将字符串转化为数字序列,然后利用 FFT 进行匹配。对于字符串的编码,可以使用类似于进制转换的方法,将字符串转化为 $k + 1$ 的一次多项式,其中第 $i$ 项的系数表示匹配错误数为 $i$ 时的字符串匹配程度。然后,利用 FFT 对两个多项式进行卷积操作,即可得到在 $k$ 次错误匹配的情况下的匹配程度。最后,再遍历每个位置的匹配程度,即可得到答案。

代码实现

详见程序注释:

// 头文件
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <complex>
using namespace std;

// 常量
const int MAXN = 2e5 + 5;
const double PI = acos(-1.0);

// 变量
int n, m, k;
char s[MAXN], t[MAXN];
int ans;

// FFT操作
inline void fft(complex<double> *a, int n, int inv = 1) {
    // 位逆序置换
    for(int i = 0, j = 0; i < n; i++) {
        if(i < j) swap(a[i], a[j]);
        for(int k = n >> 1; (j ^= k) < k; k >>= 1);
    }
    // 计算
    for(int i = 2; i <= n; i <<= 1) {
        int m = i >> 1;
        complex<double> wn(cos(2 * PI / i), inv * sin(2 * PI / i));
        for(int j = 0; j < n; j += i) {
            complex<double> w(1, 0);
            for(int k = 0; k < m; k++, w *= wn) {
                complex<double> x = a[j + k], y = w * a[j + k + m];
                a[j + k] = x + y, a[j + k + m] = x - y;
            }
        }
    }
}

// 长整数乘法
void mul(int *a, int n, int *b, int m, int *c) {
    complex<double> tmpa[MAXN], tmpb[MAXN], tmpr[MAXN], tmps[MAXN], tmpt[MAXN], tmpu[MAXN], tmpv[MAXN];
    int p = 1;
    while(p < n + m + 1) p <<= 1;
    memset(tmpa, 0, sizeof(tmpa));
    memset(tmpb, 0, sizeof(tmpb));
    for(int i = 0; i <= n; i++) tmpa[i] = a[i];
    for(int i = 0; i <= m; i++) tmpb[i] = b[i];
    fft(tmpa, p); fft(tmpb, p);
    for(int i = 0; i < p; i++) tmpr[i] = tmpa[i] * tmpb[i];
    fft(tmpr, p, -1);
    for(int i = 0; i <= n + m; i++) c[i] = (int)(tmpr[i].real() + 0.22);
}

// 多项式加法
void add(int *a, int n, int *b, int m, int *res) {
    memset(res, 0, sizeof(int) * (n + m + 1));
    for(int i = 0; i <= n; i++) res[i] += a[i];
    for(int i = 0; i <= m; i++) res[i] += b[i];
}

// 主函数
int main() {
    // 读入
    scanf("%s%s%d", s + 1, t + 1, &k);
    n = strlen(s + 1);
    m = strlen(t + 1);
    // 数据初始化
    int dp[MAXN] = {0};
    int fac[MAXN] = {0};
    int f[MAX(2 * k + 1, m) + 1] = {0};
    int g[MAX(2 * k + 1, m) + 1] = {0};
    // 循环计算匹配程度
    for(int i = 1; i <= n; i++) {
        // 计算 $s$ 的多项式表示
        memset(f, 0, sizeof(f));
        for(int j = max(0, i - k); j <= i; j++) {
            f[i - j] = (s[j] == t[i - j]) ? 1 : -1;
        }
        // 计算 $t$ 的多项式表示
        g[0] = 1;
        for(int j = 1; j <= m; j++) {
            g[j] = (t[j] == s[i - m + j]) ? 1 : -1;
        }
        // 计算卷积
        memset(dp, 0, sizeof(dp));
        for(int j = 0; j <= k && j <= i; j++) {
            mul(f + j, m - i + j + k - m, g, m, fac);
            add(dp + j, m + k - i + j, fac, m, dp + j);
        }
        // 统计答案
        ans += dp[k];
    }
    // 输出答案
    printf("%d\n", ans);
    return 0;
}

时间复杂度

由于本题利用了 FFT 算法进行求解,时间复杂度为 $O(n \log n)$。但是由于还进行了多项式乘法等操作,因此具体的常数项还需与 $k$、$m$ 等因素有关。

总结

本题主要考察了利用 FFT 求解多项式卷积的能力。因此,建议在做标准的 FFT 求解多项式卷积问题之后,再尝试做这道题。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:2019.01.26 codeforces 528D. Fuzzy Search(fft) - Python技术站

(0)
上一篇 2023年3月28日
下一篇 2023年3月28日

相关文章

  • win7系统怎么利用ASP获取服务器IP地址?

    Win7系统利用ASP获取服务器IP地址攻略 要在Win7系统上使用ASP获取服务器IP地址,你可以按照以下步骤进行操作: 创建ASP文件:首先,你需要创建一个ASP文件,可以使用任何文本编辑器,比如Notepad。将以下代码复制到ASP文件中,并保存为get_ip.asp。 <% Dim objNetwork Set objNetwork = Cre…

    other 2023年7月30日
    00
  • Android 自绘控件

    下面是详细讲解“Android 自绘控件”的完整攻略: 什么是自绘控件 自绘控件是指需要自己实现 onDraw() 方法来实现自定义绘制的控件。在 Android 中,几乎所有控件都是由系统提供的,它们的样式和尺寸都是固定的,但这样的控件往往不能满足我们的需求,因此我们需要自己定义和修改控件的样式和行为。 自绘控件的基本原理 Android 中的 View …

    other 2023年6月27日
    00
  • CMD和vbs修改 IP地址及DNS的实现代码

    CMD修改IP地址及DNS的实现代码攻略 1. 修改IP地址 要通过CMD修改IP地址,可以使用netsh命令。下面是一个示例代码: @echo off setlocal enabledelayedexpansion set \"interfaceName=以太网\" # 修改为你的网络适配器名称 set \"ipAddress…

    other 2023年7月31日
    00
  • js中Array.sort()利用零值多维排序

    首先我们要知道,Array.sort()方法是按照Unicode码点对数组进行排序的,它的默认排序顺序是将元素转换为字符串,然后比较它们对应字符的Unicode码点值。 那么,在js中,我们可以利用Array.sort()方法实现多维排序,其具体操作步骤如下: 1.以排序维度为键名对数组进行排序 假设我们现在有一个二维数组,其中包含了商品的销售信息,如下: …

    other 2023年6月25日
    00
  • 轻松理解Redux原理及工作流程

    轻松理解Redux原理及工作流程 Redux是一个流行的JavaScript状态管理库,它可以帮助我们更好地管理应用程序的状态。Redux的核心思想是将应用程序的状态存储在一个单一的、不可变的状态树中,并使用纯函数来处理状态的变化。在本攻略中,我们将详细讲解Redux的原理和工作流程。 Redux的原理 Redux的核心原理是单向数据流。当应用程序的状态发生…

    other 2023年5月6日
    00
  • 如何把pandas所有数据变成一个list

    以下是如何把pandas所有数据变成一个list的完整攻略,过程中包含两个示例说明的标准Markdown格式文本: 如何把pandas所有数据变成一个list的完整攻略 在pandas中,可以使用values属性将DataFrame或Series对象转换为NumPy数组,然后使用tolist()将数组转换为Python列表。以下是将pandas所有数据转换为…

    other 2023年5月10日
    00
  • windows 文件名太长怎么办?Windows关闭/开启短文件名功能的教程

    当Windows文件名太长时,会导致一些操作无法完成。这时可以尝试开启短文件名功能或者缩短文件名来解决问题。下面将详细介绍这两种解决方法。 问题原因及现象 当Windows文件名超过260个字符时,有些操作会因文件名太长而出现问题。出现这种情况的原因通常是由于文件夹目录结构复杂或文件名过长。 解决方法 解决这个问题的方法有两个: 开启短文件名功能 缩短文件名…

    other 2023年6月26日
    00
  • 华为荣耀9如何清理内存?华为手机内存清理教程

    华为荣耀9如何清理内存?华为手机内存清理教程 清理内存可以帮助提高华为荣耀9手机的性能和响应速度。下面是一份详细的华为手机内存清理教程,包含了两个示例说明。 步骤一:关闭不必要的后台应用 华为荣耀9手机上运行的后台应用程序可能会占用大量的内存资源。通过关闭不必要的后台应用,可以释放内存并提高手机的性能。 在主屏幕上向上滑动,打开应用抽屉。 找到并点击“设置”…

    other 2023年8月1日
    00
合作推广
合作推广
分享本页
返回顶部