题目描述

将字符串 \(T\) 拆成若干个子串,使这些子串为字符串 \(S\) 的前缀,要求拆分形成的子串数最小。

思路整理

实际上并不需要倒着枚举,也不需要线段树,更不需要 Z 函数。

如果你做过 P3002 恐吓信 这道题,不难发现他们之间的相似之处。

首先我们容易想到一个 \(O(n^2)\) 的暴力 dp 。令 \(f[i]\) 为切完前 \(i\) 个字符形成的最小子串,则可以列出下列转移方程:

\[f[i] = min(f[i-j]) + 1(0<=j<=min(|T|,i)\; and \; T(1 \sim j) == S[(i-j+1) \sim i])
\]

转移方程的实际含义就是枚举最后一刀应该砍在哪里,才能使最后一个子串为 \(T\) 的前缀,很简洁,我们尝试优化她。

首先方程的枚举只用于求最小的满足条件的 \(f[j]\) 。这很浪费。我们可以输出 \(f\) 数组进行观察,然后可以发现 \(f\) 数组单调递增。

证明一下:因为 \(S[1 \sim (i-1)]\) 是 \(S[1 \sim i]\) 的前缀,所以必然有这么长一种策略:在找到 \(S[1 \sim i]\) 的最后一个 \(T\) 的前缀时,将前缀的最后一个字母去除,就变成了\(S[1 \sim (i-1)]\) 的最后一个 \(T\) 的前缀。因此 \(f[i-1] <= f[i]\) 。

这样,我们的任务就变成了:寻找最小的满足条件的 \(j\) 。

在 P3002 中,我们的任务与这题类似。只不过 \(T\) 的所有子串都可作为魔法词缀。所以,在那题里,我们使用了后缀数组进行优化。

回到这题, 模拟一下找最远位置的步骤,发现我们要找的是最大的数 \(i\) 使 \(T\) 的长度为 \(i\) 前缀与 \(S\) 长度为 \(i\) 的后缀相等。

不如把这个问题视作将 \(T\) 在 \(S\) 上匹配。将 \(T\) 从第一位开始一位一位跟 \(S\) 的最后的字符匹配,这时我们可以发现这就是模拟了 KMP 文本串匹配模式串的过程。

从反方向理解一下:当 KMP 匹配到第 \(i\) 位时,指针 \(j\) 表示模式串匹配到前 \(j\) 位了。而与之匹配的就是文本串的后缀。

于是我们可以用 KMP 优化,在匹配途中记录下每个 \(i\) 对应的指针 \(j\) 作为能匹配的最大前缀,转移时只需要从这个转移即可。时间复杂度 \(O(|T|+|S|)\) ,瓶颈在 KMP。

代码讲解

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 1e7 + 5;
int n, m;
string T, S;
int dp[maxn], qian[maxn], f[maxn];
void kmp()
{
int j=0;
for(int i=2;i<=n;i++){
while(j&&T[j+1]!=T[i]) j=qian[j];
if(T[j+1]==T[i]) j++;
qian[i]=j;
}
j=0;
for(int i=1;i<=m;i++){
while(j&&T[j+1]!=S[i]) j=qian[j];
if(T[j+1]==S[i]) j++;
f[i] = j;
}
}
int main()
{
std::ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> n >> m;
cin >> T >> S;
T = ' ' + T;
S = ' ' + S;
for(int i = 1;i <= m;i ++) dp[i] = 19198100;
dp[0] = 0;
kmp();
for(int i = 1;i <= m;i ++)
{
dp[i] = min(dp[i], dp[i - f[i]] + 1);
}
if(dp[m] == 1919810) cout << "Fake" << endl;
else cout << dp[m] << endl;
return 0;
}

后记

本来不写题解不觉得,一写题解发现很多细节做题时并没有考虑清楚,例如单调性的证明做题时是通过打表得出的。这也证明了写题解和解题报告也是做题提升的重要一环。

P8112 符文破译的更多相关文章

  1. 长理ACM 7-密码破译(闫博钊)

    /* //题目标题: *密码破译(闫博钊) //题目描述: *某组织欲破获一个外星人的密码,密码由一定长度的字串组成.此组织拥有一些破译此密码的长度不同的钥匙,若两个钥匙的长度之和恰好为此密码的长度, ...

  2. 黑客破译android开发代码真就那么简单?

    很多程序员辛辛苦苦开发出的android开发代码,很容易就被黑客翻译了. Google似乎也发现了这个问题,从SDK2.3开始我们可以看到在android-sdk-windows\tools\下面多了 ...

  3. android 开发代码被黑客破译有那么容易吗?

    很多程序员辛辛苦苦开发出的android开发代码,很容易就被黑客翻译了. Google似乎也发现了这个问题,从SDK2.3开始我们可以看到在android-sdk-windows\tools\下面多了 ...

  4. NLP系列(1)_从破译外星人文字浅谈自然语言处理基础

    作者:龙心尘 &&寒小阳 时间:2016年1月. 出处: http://blog.csdn.net/longxinchen_ml/article/details/50543337 ht ...

  5. LOL新版符文 怎么查看队友的符文配置?

    LOL怎么看其他玩家符文? 像我这种名字都懒的打的, 直接去WeGame复制昵称. 然后在LOL主界面点生涯, 之后搜索 光头强xian生

  6. webstorm 2017.1 破译

    安装完webstorm后, 在弹出的注册窗口选择 activate > license server > 在 License server address 里输入 "http:/ ...

  7. [Bzoj5043][Lydsy1709月赛]密码破译(按位dp)

    5043: [Lydsy1709月赛]密码破译 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 477  Solved: 125[Submit][Sta ...

  8. NLP系列(1)_从破译外星人文字浅谈自然语言处理的基础

    作者:龙心尘 &&寒小阳 时间:2016年1月. 出处: http://blog.csdn.net/longxinchen_ml/article/details/50543337, h ...

  9. 【t098】符文之语

    Time Limit: 1 second Memory Limit: 128 MB [问题描述] 当小FF来到神庙时,神庙已经破败不堪了.但神庙的中央有一个光亮如新的石台.小FF走进石台, 发现石台上 ...

  10. i春秋破译

    点开题目就是一段密文 TW5650Y - 0TS UZ50S S0V LZW UZ50WKW 9505KL4G 1X WVMUSL510 S001M0UWV 910VSG S0 WFLW0K510 1 ...

随机推荐

  1. flask-基础篇02 请求与响应

    一.处理请求 1.URL路径参数(动态路由) # 例如,有一个请求访问的接口地址为/users/123,其中123实际上为具体的请求参数,表明请求123号用户的信息.此时如何从url中提取出123的数 ...

  2. python菜鸟学习: 1.用户登录输入输出

    # -*-coding: utf-8 -*-name = 'liyuzhoupan'password = '123'def login_test(): count = 0 while count &l ...

  3. vue后台管理系统——权限管理模块

    电商后台管理系统的功能--权限管理模块 1. 权限管理业务分析 通过权限管理模块控制不同的用户可以进行哪些操作,具体可以通过角色的方式进行控制,即每个用户分配一个特定的角色,角色包括不同的功能权限. ...

  4. Debug --> 使用USTC-TK2016工具对USTC-TFC2016数据集进行处理

    文件介绍: https://blog.csdn.net/u010916338/article/details/86511009?spm=1001.2101.3001.6661.1&utm_me ...

  5. hbuliter x 连接雷电模拟器

    adb  路径 E:\HBuilderX\plugins\launcher\tools\adbs 也可以配置环境变量 配置环境变量:将adb.exe所在路径,放在Path中计算机(右键)→属性→高级系 ...

  6. Jndi结合DynamicDataSource实现多数据源配置

    首先注意本框架是SSM,配置主要在两个地方.第一个是applicationContext.xml,第二个文件是Tomcat下面的context.xml里面 1.context.xml文件配置的代码如下 ...

  7. PTA两个有序链表序列的合并

    本题要求实现一个函数,将两个链表表示的递增整数序列合并为一个非递减的整数序列. 函数接口定义:   List Merge( List L1, List L2 );   其中List结构定义如下:   ...

  8. C语言初级阶段7——指针2——特殊指针

    C语言初级阶段7--指针2--特殊指针 指针函数:是一个函数,返回值类型是一个指针. #include<stdio.h> int* fun() { //a是一个局部变量 int a = 1 ...

  9. 字符流---->字符过滤流 缓冲流 : -----> printWrite用法:和BufferedReader用法

    printWrite用法:1.创建字符节点流FileWriter fw = new FileWriter("Files\\bufchar.txt");2创建字符过滤流 PrintW ...

  10. 熟悉Hadoop及其操作 3

    启动hdfs 查看与创建hadoop用户目录. 在用户目录下创建与查看input目录.   将hadoop的配置文件上传到hdfs上的input目录下.运行MapReduce示例作业,输出结果放在ou ...