2019.01.26 codeforces 528D. Fuzzy Search(fft)

yizhihongxing

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日

相关文章

  • Spring使用@Autowired为抽象父类注入依赖代码实例

    下面我将详细讲解如何使用@Autowired为抽象父类注入依赖: 前置条件 了解Java Spring框架基本概念以及注解的使用; 了解 Java代码中的抽象类的概念,以及抽象类在Spring框架中的作用。 解决问题 在使用Spring框架进行项目开发时,我们常常会使用抽象类来统一管理业务逻辑的基本流程,但在实现抽象类时,我们需要将某些依赖注入到其中,而这些…

    other 2023年6月27日
    00
  • 详解Go语言的内存模型及堆的分配管理

    详解Go语言的内存模型及堆的分配管理 Go语言是一种现代化的编程语言,它提供了一种简单而高效的内存管理模型。本文将详细讲解Go语言的内存模型以及堆的分配管理,并提供两个示例来说明。 内存模型 Go语言的内存模型基于并发原语,它允许多个goroutine(轻量级线程)同时执行。每个goroutine都有自己的栈,栈用于存储局部变量和函数调用信息。除了栈之外,G…

    other 2023年8月2日
    00
  • Java数据结构之查找

    这里是Java数据结构中查找的完整攻略。 1. 什么是查找? 在计算机科学中,查找是指在数据集合中寻找一个特定的项目,通常是为了确认其存在或位置。在Java中,常用的查找算法有线性查找、二分查找、哈希表等。 2. 线性查找 线性查找是一种简单的顺序查找方法,从第一个元素开始逐一比较,直到找到目标元素或遍历完整个数据集合。 线性查找的Java代码实现: pub…

    other 2023年6月27日
    00
  • JavaScript实现省市县三级级联特效

    JavaScript实现省市县三级级联特效攻略 简介 省市县三级级联特效是一种常见的前端开发需求,用于实现用户选择省份后,自动加载对应的城市,再选择城市后,自动加载对应的县区。本攻略将详细介绍如何使用JavaScript实现这一特效。 步骤 1. 准备数据 首先,我们需要准备省市县的数据。可以使用JSON格式的数据,例如: const data = { \&…

    other 2023年7月29日
    00
  • 几种Win7/8下创建管理员权限的CMD命令行的方法总结

    Win7/8下创建管理员权限的CMD命令行的方法有多种,下面将逐一介绍: 方法一:使用快捷键创建管理员CMD 打开“开始菜单”。 在搜索框中输入“cmd”。 鼠标右键点击“cmd.exe”。 选择“以管理员身份运行”。 此时即可在管理员权限下打开CMD命令行。 方法二:使用命令创建管理员CMD 打开“开始菜单”。 在搜索框中输入“cmd”。 在搜索结果中,找…

    other 2023年6月26日
    00
  • 通过数据库对Django进行删除字段和删除模型的操作

    在Django中,删除字段和删除模型的操作可以通过数据库进行。下面是通过数据库对Django进行删除字段和删除模型的操作的完整攻略,包括示例说明。 删除字段操作 1. 修改models.py 首先,在项目的models.py文件中将需要删除的字段注释掉,例如下面的示例: from django.db import models class MyModel(m…

    other 2023年6月25日
    00
  • ScrollView嵌套ListView滑动冲突的解决方法

    ScrollView嵌套ListView滑动冲突的解决方法 当我们在Android开发中需要在一个ScrollView中嵌套一个ListView时,可能会遇到滑动冲突的问题。这是因为ScrollView和ListView都具有滑动功能,导致它们之间的滑动事件冲突。下面是解决这个问题的完整攻略。 方法一:自定义ListView 一种解决方法是自定义一个List…

    other 2023年7月28日
    00
  • 红米手机如何关闭开发者模式?红米手机关闭开发者模式教程

    红米手机如何关闭开发者模式? 在红米手机中,关闭开发者模式非常简单,只需按照以下步骤进行操作即可。 步骤一:进入设置页面 首先,我们需要进入红米手机的设置页面。可以通过在桌面上点击“设置”图标来打开设置页面。 步骤二:进入开发者选项 在设置页面中向下滚动,找到“关于手机”或“系统”选项。然后,在“关于手机”或“系统”页面中向下滚动,找到“MIUI版本号”选项…

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