1月10号自习总结

刷题

无线传输

[BOI2009]Radio Transmission 无线传输 - 洛谷

题目描述

给你一个字符串 s1​,它是由某个字符串 s2​ 不断自我连接形成的。但是字符串 s2是不确定的,现在只想知道它的最短长度是多少。

输入格式

第一行一个整数 L,表示给出字符串的长度。

第二行给出字符串 s1​ 的一个子串,全由小写字母组成。

输出格式

仅一行,表示 s2​ 的最短长度。

输入输出样例

输入 #1

8
cabcabca

输出 #1

3

说明/提示

样例输入输出 1 解释

对于样例,我们可以利用 abcabc 不断自我连接得到 abcabcabcabcabcabc,读入的 cabcabcacabcabca,是它的子串。

规模与约定

对于全部的测试点,保证 1<L≤1061<L≤106。

 算法:KMP

思路:读题,给出一个由循环串组成字符串的子串,通过这个子串求这个字符串的最小循环串。直接举例子,字符串:123412341234,明显最小循环串是1234,

假如给出子串:41234,可以看出4是最长重复前后缀,这题的关键就在于重复的理解,既然它重复了,那么该子串里面肯定由一个完整的最小循环串和最小循环串中的一端组成,而这个一端就是重复的前后缀的其中一缀,于是只要用该子串的长度减去这一端的长度就是最小循环串的长度

代码:

#include<stdio.h>
#include<string.h>
//#include<stdlib.h>
#define gy 1200000
char ft[gy] = {"\0"};
int next[gy];//TMD,为什么动态分配一个对应长度的next数组就会超时,只能直接定义一个next数组(—_—)?
void next_cre(char *ptr, int *next) {//计算next数组
	int plen = strlen(ptr);//计算模式串长度
	int k = -1;
	next[0] = -1;//q从1开始,故初始化给其赋值-1
	for (int q = 1; q < plen ; q++) {
		while (k > -1 && ptr[k + 1] != ptr[q]) {//部分匹配
			k = next[k];//将k往前移动
		}
		if (ptr[k + 1] == ptr[q]) {//找到匹配的
			k++;//k往前移动一个单位
		}
		next[q] = k;
	}
}
int main() {
	int n;
	scanf("%d", &n);
	getchar();//切记一定要吸收换行符,否则TM又要花上一个上午的时间去找该死的bug
	scanf("%s", ft);
	//int *next=(int*)malloc(sizeof(strlen(ft)));//分配所需长度的数组
	next_cre(ft, next);
	/*
	for (int i = 0; i < n; i++) {
		printf("%d ", next[i]+1);
	}
	printf("\n");
	*/
	printf("%d", n - 1 - next[n - 1]);//子串长度减去重复缀长度就是最小循环串长度
	//free(next);
	return 0;
}

Compress Words

Compress Words - 洛谷

题目描述

Amugae has a sentence consisting of nn words. He want to compress this sentence into one word. Amugae doesn't like repetitions, so when he merges two words into one word, he removes the longest prefix of the second word that coincides with a suffix of the first word. For example, he merges "sample" and "please" into "samplease".

Amugae will merge his sentence left to right (i.e. first merge the first two words, then merge the result with the third word and so on). Write a program that prints the compressed word after the merging process ends.

输入格式

The first line contains an integer nn ( 1≤n≤1051≤n≤105 ), the number of the words in Amugae's sentence.

The second line contains nn words separated by single space. Each words is non-empty and consists of uppercase and lowercase English letters and digits ('A', 'B', ..., 'Z', 'a', 'b', ..., 'z', '0', '1', ..., '9'). The total length of the words does not exceed 106106 .

输出格式

In the only line output the compressed word after the merging process ends as described in the problem.

题意翻译

Amugae有n个单词,他想把这个n个单词变成一个句子,具体来说就是从左到右依次把两个单词合并成一个单词.合并两个单词的时候,要找到最大的i(i≥0)i(i≥0),满足第一个单词的长度为ii的后缀和第二个单词长度为ii的前缀相等,然后把第二个单词第ii位以后的部分接到第一个单词后面.输出最后那个单词

输入输出样例

输入 #1

5
I want to order pizza

输出 #1

Iwantorderpizza

输入 #2

5
sample please ease in out

输出 #2

sampleaseinout

 算法:KMP

思路:

将每一个新单词放入一个暂时的数组,再将已经连接好了的单词放在暂时数组中的新单词后面组成一个临时字符串,求这个的最长重复前后缀的长度,最后将这个新单词减去这个长度的剩下的部分与已经连接好的单词连接,将所有新单词连接完了之后即可输出最后那个单词

代码:

#include<stdio.h>
#define LEN 106107
#include<string.h>
char all[LEN], temp[LEN];
int al = 0, nxt[LEN]; //不能命名为next数组,因为next是关键词
int next_ad(char ptr[], int len) { //返回重复前缀的最后一个字符下标,ptr为临时数组,len为新单词长度
	int plen = strlen(ptr);
	memset(nxt, -1, sizeof(plen));//初始化
	int k = -1;
	for (int i = plen - len; i < plen; i++) {
		while (k > -1 && ptr[k + 1] != ptr[i]) {
			k = nxt[k];
		}
		if (ptr[k + 1] == ptr[i]) {
			k++;
		}
		nxt[i] = k;
	}
	return nxt[plen - 1];
}
int main() {
	int n;
	scanf("%d", &n);
	getchar();
	scanf("%s", &all);//all数组中放入第一个单词
	al += strlen(all);
	for (int i = 1; i < n; i++) {
		char tt[50] = {"\0"}; //暂时存放单词的地方
		scanf("%s", &tt);
		memset(temp, 0, sizeof(temp)); //存放临时字符串的数组初始化为空
		strcpy(temp, tt);//将新单词复制到临时数组上去
		int lentt=strlen(tt);
		for(int iu=0;iu<5;iu++){//防止最大匹配长度超过待拼接串的长度。
			temp[lentt++]='#';
		}
		strcat(temp, all);//将已经连接的单词与新单词连接起来,新单词放在最前面
		int jh = next_ad(temp, lentt) + 1;//jh为重复部分的长度
		while (jh < strlen(tt)) {//将新单词不重复的那一部分连在已经连接好了的单词上去
			all[al++] = tt[jh++];
		}
	}
	printf("%s", all); //输出最后的单词
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值