完全背包,KMP字符串匹配问题

 P1616 疯狂的采药

题目背景

此题为纪念 LiYuxiang 而生。

题目描述

LiYuxiang 是个天资聪颖的孩子,他的梦想是成为世界上最伟大的医师。为此,他想拜附近最有威望的医师为师。医师为了判断他的资质,给他出了一个难题。医师把他带到一个到处都是草药的山洞里对他说:“孩子,这个山洞里有一些不同种类的草药,采每一种都需要一些时间,每一种也有它自身的价值。我会给你一段时间,在这段时间里,你可以采到一些草药。如果你是一个聪明的孩子,你应该可以让采到的草药的总价值最大。”

如果你是 LiYuxiang,你能完成这个任务吗?

此题和原题的不同点:

11. 每种草药可以无限制地疯狂采摘。

22. 药的种类眼花缭乱,采药时间好长好长啊!师傅等得菊花都谢了!

输入格式

输入第一行有两个整数,分别代表总共能够用来采药的时间 t 和代表山洞里的草药的数目 m。

第 22 到第 (m+1) 行,每行两个整数,第(i+1) 行的整数 ai​,bi​ 分别表示采摘第 i 种草药的时间和该草药的价值。

输出格式

输出一行,这一行只包含一个整数,表示在规定的时间内,可以采到的草药的最大总价值。

输入输出样例

输入 #1复制

70 3
71 100
69 1
1 2

输出 #1复制

140

说明/提示

数据规模与约定
  • 对于 30%30% 的数据,保证 m≤103 。
  • 对于 100%100% 的数据,保证 1≤m≤104,1≤t≤107,且 1≤m×t≤107,1≤ai​,bi​≤104。

完全背包和01背包的区别是,完全背包的物品可以无限的取,而01背包的物品只能取一次。

那么在做的时候,只需把遍历的顺序更改一下,就可以实习完全背包,01背包是倒序取值,完全背包是正序取值,我们来看看代码。 

 

#include <iostream>
#include <bits/stdc++.h>
using namespace std;
long long dp[10000010];
int w[10010];
int v[10010];
int main()
{ int m,t;
   cin>>t>>m;
    for(int i=0;i<m;i++)
    cin>>w[i]>>v[i];
    for(int i=0;i<m;i++)
    {
        for(int j=w[i];j<=t;j++)//正序取值,与01背包不同点
        {
            dp[j]=max(dp[j],dp[j-w[i]]+v[i]);//状态转移方程,用一维数组实现dp
        }
    }
    cout <<dp[t]<< endl;
    return 0;
}

KMP字符串匹配问题

P3375 【模板】KMP

题目描述

给出两个字符串 s1​ 和 s2​,若 s1​ 的区间 [l,r] 子串与 s2​ 完全相同,则称s2​ 在 s1​ 中出现了,其出现位置为 l。
现在请你求出 s2​ 在 s1​ 中所有出现的位置。

定义一个字符串 s 的 border 为 s 的一个非 s 本身的子串 t,满足 t 既是 s 的前缀,又是 s 的后缀。
对于 s2​,你还需要求出对于其每个前缀 's′ 的最长 border ′t′ 的长度。

输入格式

第一行为一个字符串,即为 s1​。
第二行为一个字符串,即为 s2​。

输出格式

首先输出若干行,每行一个整数,按从小到大的顺序输出 s2​ 在 s1​ 中出现的位置。
最后一行输出 ∣s2​∣ 个整数,第 i 个整数表示 s2​ 的长度为 i 的前缀的最长 border 长度。

输入输出样例

输入 #1复制

ABABABC
ABA

输出 #1复制

1
3
0 0 1 

说明/提示

样例 1 解释

对于 s2​ 长度为 33 的前缀 ABA,字符串 A 既是其后缀也是其前缀,且是最长的,因此最长 border 长度为 11。

数据规模与约定

本题采用多测试点捆绑测试,共有 3 个子任务

  • Subtask 1(30 points):∣s1​∣≤15,∣s2​∣≤5。
  • Subtask 2(40 points):∣s1​∣≤104,∣s2​∣≤102。
  • Subtask 3(30 points):无特殊约定。

对于全部的测试点,保证 1≤∣s1​∣,∣s2​∣≤106,s1​,s2​ 中均只含大写英文字母。

首先,什么是KMP算法,模式串匹配,就是给定一个需要处理的文本串(理论上应该很长)和一个需要在文本串中搜索的模式串(理论上长度应该远小于文本串),查询在该文本串中,给出的模式串的出现有无、次数、位置等。

我们学KMP之前得了解前缀和后缀,那么什么是前后缀呢?

举个例子: ABACD

前缀有:A,AB,ABA,ABAC,ABACD

后缀有:D,CD,ACD,BACD,ABACD 

那么有了前后缀后如何来进行KMP算法呢,我们还要找出它们的最大相等前后缀,什么是最大相等前后缀,举个例子:ABABC

那么我们来看当下标是2(0开头)时,前缀有,A,AB,ABA

                                                              后缀有,A,BA,ABA

那么它最大相等前后缀为3(ABA)简单点就是找最大长度且相等的子串。

比如 ABABC  因为文本串不好操作,所以只能在模式串里进行查找最大相等前后缀

        0 0 1 2 0 这就是它的最大相等前后缀,它有什么用呢?

模式串:abcab
文本串:abcacababcab

它的最大前后缀为 0 0 0 1 2 

我们来模拟一遍查找

到模式串第五个字符时不同,这时我们不用把文本串的指针回退,只需要在模式串里找到与失配的字符相同的就可以了,怎么找,根据之前的最大相等前后缀来查找,模式串下有个2,只需模式串回退到第二个字符继续判断,如果还是不相等下标0,回退到第一个字符重新查找。这就是KMP算法,相比于朴素算法,KMP是线性的查找节约了大量的时间,最后我们来看看带代码。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char a[1000010],b[1000010];
int next[1000010];
int k,k1;
void getnext()//最大相等前后缀的初始值
{
    next[0]=0;
    int i=1,j=0;
    while(i<k1)
    {
        if(b[i]==b[j])
        {
            next[i++]=++j;
        }
        else
            {
            if(j==0)
            {
            next[i++]=0;
            }
        else
            {
            j=next[j-1];
            }
        }

    }
}
int main()
{
   scanf("%s%s",a,b);
   k=strlen(a),k1=strlen(b);
   getnext();
   int i,j;
   i=j=0;
   while(i<k)
   {
       if(a[i]==b[j])//相等下一位
       {
           ++i;
           ++j;
       }
       else
        {if(j>0)
        j=next[j-1];//如果失配,模式串根据下标移到与文本串失配字符相同的下标
       else
        ++i;}//特判,第一位就失配
      if(j>=k1)//满足条件,输出
        {
            printf("%d\n",i-j+1);
            j=next[j-1];//j指向了'\0'失配,移动到文本串所指字符的下标
        }
   }
  for(int i=0;i<k1;i++)
     printf("%d ",next[i]);
    return 0;
}

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值