hdu 2087

KMP算法详解与实践
剪花布条
Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 20118    Accepted Submission(s): 12579




Problem Description
一块花布条,里面有些图案,另有一块直接可用的小饰条,里面也有一些图案。对于给定的花布条和小饰条,计算一下能从花布条中尽可能剪出几块小饰条来呢?
 


Input
输入中含有一些数据,分别是成对出现的花布条和小饰条,其布条都是用可见ASCII字符表示的,可见的ASCII字符有多少个,布条的花纹也有多少种花样。花纹条和小饰条不会超过1000个字符长。如果遇见#字符,则不再进行工作。
 


Output
输出能从花纹布中剪出的最多小饰条个数,如果一块都没有,那就老老实实输出0,每个结果之间应换行。
 


Sample Input
abcde a3
aaaaaa  aa
#
 


Sample Output
0
3
 


思路:kmp() 主要是nxt数组的获得,并且在提交的时候选的G++,不能用next变量名啊,会CE啊

代码:尚未优化的kmp代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <set>
#include <map>
#include <algorithm>
#include <vector>
#include <cmath>

using namespace std;
const int max_size = 1005;
string a,b;
int nxt[max_size];

void  get(){
    int j = -1,i = 0;
    nxt[0] = -1;
    int len = b.size();
    while(i < len){
        if(j == -1 || b[i] == b[j]){
            i++;
            j++;
            nxt[i] = j;
        }else{
            j = nxt[j];
        }
    }
}

void kmp(){
    int len1 = a.size();
    int len = b.size();
    int j = 0;
    int i = 0;
    int ans = 0;
    get();
    while(i < len1){
        if(j == -1 || a[i] == b[j]){
            ++i;
            ++j;
        }else{
           j  = nxt[j];
        }
        if(j >= len){
            ans++;
            j = 0;
        }
    }
    cout << ans << endl;
}
int main() {
    while(cin >> a && a != "#"){
        cin >> b;
        memset(nxt,0,sizeof(nxt));
        kmp();
    }
    return 0;
}

优化的代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <set>
#include <map>
#include <algorithm>
#include <vector>
#include <cmath>

using namespace std;
const int max_size = 1005;
string a,b;
int nxt[max_size];

void  get(){
    int j = -1,i = 0;
    nxt[0] = -1;
    int len = b.size();
    while(i < len){
        if(j == -1 || b[i] == b[j]){
            i++;
            j++;
            if(b[i] == b[j]){
                nxt[i] = nxt[j];
            }else{
                j = nxt[j];
            }
        }else{
            j = nxt[j];
        }
    }
}

void kmp(){
    int len1 = a.size();
    int len = b.size();
    int j = 0;
    int i = 0;
    int ans = 0;
    get();
    while(i < len1){
        if(j == -1 || a[i] == b[j]){
            ++i;
            ++j;
        }else{
           j  = nxt[j];
        }
        if(j >= len){
            ans++;
            j = 0;
        }
    }
    cout << ans << endl;
}
int main() {
    while(cin >> a && a != "#"){
        cin >> b;
        memset(nxt,0,sizeof(nxt));
        kmp();
    }
    return 0;
}


### 数据范围说明 输入数据为多组,每组包含两个由可见 ASCII 字符组成的字符串,分别代表花布条和小饰条。每个字符串的长度不超过 1000 个字符。当输入的花布条为 "#" 时,程序停止处理后续输入 [^2]。 ### 完整 C++ 程序 #### 使用 KMP 算法的实现 ```cpp #include<bits/stdc++.h> using namespace std; typedef long long ll; const int N = 1e7; int Next[N]; // 计算 Next 数组 void getNext(string s, int Next[]) { Next[0] = -1; int i = 0, j = -1; while (i < s.size()) { if (j == -1 || s[i] == s[j]) { i++; j++; if (s[i] == s[j]) Next[i] = Next[j]; else Next[i] = j; } else j = Next[j]; } } // KMP 匹配函数 int kmp(string t, string p) { getNext(p, Next); int i = 0, j = 0, ans = 0; while (i < t.size()) { if (j == -1 || t[i] == p[j]) { i++; j++; if (j == p.size()) { ans++; j = 0; } } else j = Next[j]; } return ans; } int main() { string t, p; while (cin >> t) { if (t == "#") break; else cin >> p; cout << kmp(t, p) << endl; } return 0; } ``` #### 使用 `strstr` 函数的实现 ```cpp #include<iostream> #include<string.h> using namespace std; int main() { char s1[1010], s2[1010]; string s3 = "#"; while (cin >> s1, s1 != s3) { cin >> s2; int n = 0; int len2 = strlen(s2); char* p = s1; while (strlen(p) >= len2) { p = strstr(p, s2); if (p != 0) { n++; p += len2; } else break; } cout << n << endl; } return 0; } ``` ### 详细思考过程 #### 思路概述 该问题的核心是在一个较长的字符串(花布条)中找出另一个较短字符串(小饰条)的出现次数,且每次匹配成功后要从匹配位置之后继续寻找下一个匹配,以确保小饰条是完整且不重叠的。 #### KMP 算法思路 1. **Next 数组计算**:Next 数组用于记录模式串(小饰条)中每个位置之前的子串的最长公共前后缀长度。通过 `getNext` 函数,从模式串的开头开始,利用前后缀指针 `i` 和 `j` 不断比较字符,根据比较结果更新 Next 数组的值,以便在匹配失败时能快速回退模式串指针 [^1]。 2. **KMP 匹配过程**:在 `kmp` 函数中,使用两个指针 `i` 和 `j` 分别遍历文本串(花布条)和模式串。当字符匹配时,两个指针同时后移;当匹配失败时,模式串指针 `j` 根据 Next 数组回退。当模式串完全匹配时,匹配次数 `ans` 加 1,模式串指针 `j` 回退到开头,继续在文本串中寻找下一个匹配 [^1]。 3. **主函数处理输入**:在 `main` 函数中,使用 `while` 循环读取多组输入,直到遇到 "#" 为止。对于每组输入,调用 `kmp` 函数计算小饰条在花布条中的出现次数并输出 [^1]。 #### `strstr` 函数思路 1. **使用 `strstr` 查找子串**:`strstr` 函数用于在一个字符串中查找另一个字符串的第一次出现位置。在 `main` 函数中,通过不断调用 `strstr` 函数在花布条中查找小饰条。 2. **统计匹配次数**:每次找到匹配后,匹配次数 `n` 加 1,并将指针移动到匹配位置之后,继续查找下一个匹配。当剩余字符串长度小于小饰条长度或找不到匹配时,停止查找并输出匹配次数 [^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值