ZJM与生日礼物
ZJM 收到了 Q老师 送来的生日礼物,但是被 Q老师 加密了。只有 ZJM 能够回答对 Q老师 的问题,Q老师 才会把密码告诉 ZJM。
Q老师 给了 ZJM 一些仅有 01 组成的二进制编码串, 他问 ZJM:是否存在一个串是另一个串的前缀.
Input
多组数据。每组数据中包含多个仅有01组成的字符串,以一个9作为该组数据结束的标志。
Output
对于第 k 组数据(从1开始标号),如果不存在一个字符串使另一个的前缀,输出"Set k is immediately decodable",否则输出"Set k is not immediately decodable"。
每组数据的输出单独一行
Sample Input
01
10
0010
0000
9
01
10
010
0000
9
Sample Output
Set 1 is immediately decodable
Set 2 is not immediately decodable
解题思路(垃圾话)
字典树我以前也是学过的,但是学了之后就没怎么用过,因为打比赛遇到字符串题一旦不是签到题就等于没有这题了(还是太菜了)
这道题直接问的就是前缀问题,有两种情况。
①是否已经存在当前串的前缀。因为每次往树上插入都存了终点标记,所以在跑当前串时看看有没有这个标记就行了。
②当前串是否为已经存在的串的前缀。在插入当前串时判断最后一个字母是否已经存在就可以了。
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <map>
#define ll long long
const int maxn = 100005;
using namespace std;
struct trie
{
static const int N = 1010,charset = 2;
int tot,root,child[N][charset],flag[N];
trie()
{
memset(child,-1, sizeof(child));
root = tot = 0;
}
void clear()
{
memset(child,-1, sizeof(child));
root = tot = 0;
}
bool insert(char *str)
{
int now = root,len = strlen(str);
for(int i=0;i<len;i++)
{
int x = str[i] - '0';
if(child[now][x]==-1)
{
child[now][x] = ++tot;
flag[now] = 0;
}
else if(i==len-1 || flag[child[now][x]])return true;
now = child[now][x];
}
flag[now] = 1;
return false;
}
};
char s[maxn];
int main()
{
int cnt = 0;
bool vis = false;
trie trietree;
while (scanf("%s",s)!=EOF)
{
if(s[0]=='9')
{
cnt++;
printf("Set %d is ",cnt);
if(vis)printf("not ");
printf("immediately decodable\n");
vis = false;
trietree.clear();
}
else
{
vis = trietree.insert(s);
}
}
return 0;
}
ZJM与纸条
ZJM 的女朋友是一个书法家,喜欢写一些好看的英文书法。有一天 ZJM 拿到了她写的纸条,纸条上的字暗示了 ZJM 的女朋友 想给 ZJM 送生日礼物。ZJM 想知道自己收到的礼物是不是就是她送的,于是想看看自己收到的礼物在纸条中出现了多少次。
Input
第一行输入一个整数代表数据的组数
每组数据第一行一个字符串 P 代表 ZJM 想要的礼物, 包含英语字符 {‘A’, ‘B’, ‘C’, …, ‘Z’}, 并且字符串长度满足 1 ≤ |P| ≤ 10,000 (|P| 代表字符串 P 的长度).
接下来一行一个字符串 S 代表 ZJM 女朋友的纸条, 也包含英语字符 {‘A’, ‘B’, ‘C’, …, ‘Z’}, 满足 |P| ≤ |S| ≤ 1,000,000.
Output
输出一行一个整数代表 P 在 S中出现的次数.
Sample Input
3
BAPC
BAPC
AZA
AZAZAZA
VERDI
AVERDXIVYERDIAN
Sample Output
1
3
0
KMP字符串匹配问题。以前也学过KMP,但是很久不用也忘了,再学一次没差。
核心思想是当出现s与p失配的情况后利用nxtpos[]数组实现只移动p而不移动s上的位置指针i,所谓nxtpos数组,nxtpos[i]表示在字符串p上的位置i之前(不包括i)的字串中最大公共前缀后缀的长度(如p=ABCAB,nxtpos[4]=1,最大公共前缀后缀就是’A’。
求nxtpos的过程大概 就是个p与自己匹配的过程,个人理解,k是在完成一个不断跑前缀的任务,用来计算nxtpos[j],有一个小优化就是p[j]==p[k]的时候,由于原来的算法中k就是nxtpos[j],会导致s[i]与p[j]失配后还是会和p[k]失配,这时就继续递归使nxtpos[p]=nxtpos[k]。
然后nxtpos就求好了。
然后用nxtpos匹配,失配时让j=nxtpos[j]
真的很神奇
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <map>
#define ll long long
const int maxn = 10005;
const int maxm = 1000005;
using namespace std;
int n;
int nxt[maxn];
char ptr[maxn],str[maxm];
void getnxt(const char s0[],int len)
{
nxt[0] = 0;
for(int i=1,j=0;i<len;i++)
{
while (j && s0[j] != s0[i])j = nxt[j-1];
if(s0[j] == s0[i])j++;
nxt[i] = j;
}
}
int kmp()
{
int len1 = strlen(str);
int len2 = strlen(ptr);
int j = 0;
int ans = 0;
getnxt(ptr,len2);
for(int i=0;i<len1;i++)
{
while(j && ptr[j]!=str[i])j=nxt[j-1];
if(ptr[j] == str[i])j++;
if(j==len2)
{
ans++;
j = nxt[j-1];//仔细看看那个套娃一样的样例,匹配完成要回到上一个搭配的位置才能保证不漏答案
}
}
return ans;
}
int main()
{
cin>>n;
while (n--)
{
memset(ptr,0, sizeof(ptr));
memset(str,0, sizeof(str));
memset(nxt,0, sizeof(nxt));
scanf("%s",ptr);
scanf("%s",str);
printf("%d\n",kmp());
}
return 0;
}