JZOJ4316【NOIP2015模拟11.5】Isfind 题解

JZOJ4316 【NOIP2015模拟11.5】Isfind 题解

Description

Input

Output

Sample Input

4 3
    acbc
    abc
    cba
    cc

Sample Output

Y
    N
    Y

Data Constraint

思路:

题意要看懂,首先声明一下“子串”和“子序列”的区别,S的“子串”意思是在S中选取任意i个(0 < i <= |S|)连续字符组成的字符串,而S的“子序列”意为在S中任选i个字符,按原来的顺序连接而成的字符串。举个例子,对于字符串"abaacba","baac"是这个字符串的子串,"bca"不是这个字符串的子串而是这个字符串的子序列。

那么看题,我们需要实现的算法就是判断一个字符串是否为另一个字符串的子序列。

1.暴力算法,贪心思想。

考虑遍历每次给出的字符串,如果给出的这个字符串是S的子序列,那么S必然包含这个字符串的每个字符,并且这些先后顺序与给出的字符串一致。对于每个给出的字符串T,遍历每个Ti,在S中找到从先到后的第一个Sj=Ti,并且满足j要在上一个匹配的字符后,如此循环往复,如果对于每个Ti都能找到对应的Si,那么T就是S的子序列,匹配成功,反之亦然。

看文字看不明白?根据样例讲吧:

将T=‘abc‘和S=‘acbc‘匹配,首先在S中找到‘a‘第一次出现的位置,位置为1,匹配成功,然后在S中找在1之后‘b‘第一次出现的位置,位置为3,匹配成功,然后在S中找在3之后‘c‘第一次出现的位置,位置为4,匹配成功,所有字符全部匹配。

将T=‘cba‘和S=‘acbc‘匹配,首先在S中找到‘c‘第一次出现的位置,位置为2,匹配成功,然后在S中找在2之后‘b‘第一次出现的位置,位置为3,匹配成功,然后在S中找在3之后‘a‘第一次出现的位置,‘a‘在3之后没有出现过,匹配失败。

要明白,每次之所以跳第一次出现的位置,是为了使S剩下可以匹配的字符尽量多,使尽量多的字符参加匹配。

复杂度分析,对于每个T,其与S匹配时,最坏情况下S的每个字符都要参与遍历,时间复杂度为O(n),m次匹配的复杂度就是O(nm),超时。

2.暴力之上的优化。

观察复杂度式子O(nm),显然O(m)的复杂度是避免不了的,于是我们可以尝试在n上面做些文章。

暴力过程中,我们一步一步跳S来找从位置i开始字母c第一次出现的位置,但是S与询问无关,每次询问都是与S匹配,S是独立于询问的,也就意味着如果S给出了,从位置i开始字母c第一次出现的位置就是固定的了。于是就能预处理搞了。

设一个f[i][j]表示从i位置开始,j字母第一次出现的位置,易得转移方程为

1.f[i][j] = i (s[i] == j)

2.f[i][j] = f[i + 1][j] (s[i] != j)

转移时i需倒序枚举,因为f[i][j]需要用到f[i + 1][j]的东西。

复杂度的话,预处理复杂度为O(26n)常数可以省略即O(n),对于m次询问,每次询问只需将读入的字符串T遍历一遍,复杂度为O(|T|)总共这m次询问加起来就是所有T的长度之和,题目给出的总长度<=4*10^6,可以视为线性的复杂度,线性加线性,于是总复杂度可视为O(n)。

听不懂的话可以根据代码理解哦:

 1 #include <cmath>
 2 #include <cstdio>
 3 #include <cstring>
 4 #include <cstdlib>
 5
 6 const int N = 1e5 + 3, M = 4e6 + 7;
 7
 8 int n, q, po, len;
 9 char str[M];
10 int f[N][26]; //注意这里不能开f[M][26],空间超限
11
12 int main()
13 {
14     freopen("isfind.in", "r", stdin); //无视文件操作
15     freopen("isfind.out", "w", stdout); //无视文件操作
16
17     memset(f, 0xff, sizeof(f)); //0xff经过memset以后就是0xffffffff即-a
18     scanf("%d%d%s", &n, &q, str + 1); //方便处理,从1开始
19     for (int i = n; i >= 1; i--) //倒序枚举
20         for (int j = 0; j < 26; j++)
21             if (j + ‘a‘ == str[i]) f[i][j] = i; //如上所述
22             else f[i][j] = f[i + 1][j];
23     while (q--)
24     {
25         scanf("%s", str + 1);
26         po = 1, len = strlen(str + 1); //从1开始匹配
27         for (int i = 1; i <= len; i++)
28         {
29             po = f[po][str[i] - ‘a‘]; //找到第一个出现的位置
30             if (po == -1) //不匹配
31             {
32                 printf("N\n");
33                 break;
34             }
35             else po++; //移到下一位继续匹配
36         }
37         if (po != -1) printf("Y\n"); //匹配
38     }
39
40     fclose(stdin);
41     fclose(stdout);
42     return 0;
43 }

有问题请留言。

原文地址:https://www.cnblogs.com/zjlcnblogs/p/8343716.html

时间: 2024-10-02 17:57:11

JZOJ4316【NOIP2015模拟11.5】Isfind 题解的相关文章

[总结+题解]NOIP2015模拟题2

// 此博文为迁移而来,写于2015年7月22日,不代表本人现在的观点与看法.原始地址:http://blog.sina.com.cn/s/blog_6022c4720102w72i.html 1.总结 向总表示今天的题目简单些,恩我觉得我又单纯了.今天的分数略低啊,第三题的动规只有10分,第一题的暴力也TLE了一堆,最后就剩下个150分了. 2.题解 T1 坐船问题(TAG:结论题) 简直就是一道结论题啊!这道题不存在什么算法.最开始想到的是二分图匹配,但是过于繁琐,而且已经证出存在反例.首先

模拟11题解

T1[A. string]「桶排序」「线段树」 线段树维护区间的每个字母出现了多少次, 在排序的时候,先查询一个区间的每个字母的出现次数,然后挨个区间赋值 复杂度  $O(mlog(n)*26)$ 优化常数(26):定义f(懒标记):f!=0时,代表子树都被赋值为了同一个值;f==0,表示不相等. 将区间查询改为只有访问到了f!=0再对ans贡献return,pushup,pushdown同理,避免了枚举26 当然也可以循环展开勉强卡过 1 #include<iostream> 2 #incl

模拟11 题解

A. string 类似 HEOI2016排序 . 排序这道题因为只询问单点最终答案,二分答案, 将小于和大于等于答案的数分别设为0 1, 用线段树维护0 1的排序即可. 算法一: 本题中的1-n变成了0-25(即a-z),单点询问变成了全体询问. 仿照排序那道题的做法,线段树优化桶排序. 维护每个节点每个字符的cnt值,区间查询后区间赋值即可. 然而up和down函数都是$O(26)$的,加上修改操作最多被拆分为26个, 总的复杂度为$O(nlogn26^2)$. 需要卡常. 考场上码出了80

省选模拟11 题解

A. 组合数问题 还没想明白如何做,待补. B. recollection 因为原图为trie树,树上两个点的lcp长度等于两个点的lca深度. 考虑通过广义sam来维护两个点的lcs. 树上同时对应着一个$endpos$,树上两个点对应的$endpos$对应的广义sam上节点在后缀树的lca的$len$即为两个点的lcs长度. 所以对于原树上每个点,我们只关注它的子树在后缀树上能形成的$len$最大的lca. 树上$n$个点形成的lca集合,实际上等于dfs序上相邻的两点形成的lca集合. 所

8.11联考题解

样例输入: 3 6 0 -1 1 1 0 -1 -1 1 0 1 2 3 1 2 3 样例输出: 3 题解      不要看上面那个吓人的时间限制--实际上内网给了4Sec,高明的模拟能过:外网给的时间比这还多,直接暴力模拟就能A,这就是为什么我今天成绩莫名高了不少.刚开始就只想到了模拟,用链表可以稍微优化一点有限,然而时间效率近似于n*答案,要依靠答案的大小来决定时间效率,让我不由得想到某名为<天鹅会面>的惨痛事故.想了很久还是没想出来,直接把模拟当做骗分程序来写,连快读都没有写:自认为是今

BestCoder Round #11 (Div. 2) 题解

HDOJ5054 Alice and Bob Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 302    Accepted Submission(s): 229 Problem Description Bob and Alice got separated in the Square, they agreed that if they

[jzoj]4216.【NOIP2015模拟9.12】平方和

Link https://jzoj.net/senior/#main/show/4216 Description 给出一个N个整数构成的序列,有M次操作,每次操作有一下三种: ①Insert Y X,在序列的第Y个数之前插入一个数X: ②Add L R X,对序列中第L个数到第R个数,每个数都加上X: ③Query L R,询问序列中第L个数到第R个数的平方和. Solution 我不会告诉你这道题我打了10000+byte,并且改了2个月,50多个小时,删掉代码重打了5次.这道题用splay来

FOJ 11月月赛题解

抽空在vjudge上做了这套题.剩下FZU 2208数论题不会. FZU 2205 这是个想法题,每次可以在上一次基础上加上边数/2的新边. 1 #include <iostream> 2 #include <vector> 3 #include <algorithm> 4 #include <string> 5 #include <string.h> 6 #include <stdio.h> 7 #include <queue

11月下旬题解

(压根就没做几道题,无地自容QAQ) gym101138 J 直接上树链剖分+线段树,注意处理好端点问题(其实有(Q+N)logN的做法) 1 #include<bits/stdc++.h> 2 3 using namespace std; 4 typedef long long ll; 5 struct way{int po,next;} e[200010]; 6 struct node{ll lm,rm,s,mx;} tr[100010*4]; 7 int s[100010],a[1000