程序设计实习 第三讲 字符串处理

40
程程程程程程 程程程 程程程程程

Upload: quang

Post on 06-Jan-2016

92 views

Category:

Documents


2 download

DESCRIPTION

程序设计实习 第三讲 字符串处理. 内容提要. 字符串的存储、字符串处理函数 将数组传递给函数 例题: ai2767 Caesar 密码 (P115) 例题:单词排序 例题: ai2744 子串 (P112) 例题: POJ1936 All in All. 字符串. 每个字符串是一个特殊的数组,满足两个条件 元素的类型为 char 最后一个元素的值为‘ \0’ ,Ascii 码就是 0 以字符型数组存储 从 0 号元素开始存储 最大可以存储长度为 N-1 的字符串, N 是数组的大小。 - PowerPoint PPT Presentation

TRANSCRIPT

Page 1: 程序设计实习 第三讲 字符串处理

程序设计实习

第三讲 字符串处理

Page 2: 程序设计实习 第三讲 字符串处理

内容提要 字符串的存储、字符串处理函数 将数组传递给函数 例题: ai2767 Caesar密码 (P115)

例题:单词排序 例题: ai2744 子串 (P112) 例题: POJ1936 All in All

Page 3: 程序设计实习 第三讲 字符串处理

字符串 每个字符串是一个特殊的数组,满足两个条件

元素的类型为 char 最后一个元素的值为‘ \0’ ,Ascii 码就是 0

以字符型数组存储 从 0 号元素开始存储 最大可以存储长度为 N-1 的字符串, N 是数组的

大小。 字符串“ hello” 在长度为 10 的字符串数组中的存储

h e l l o \0

Page 4: 程序设计实习 第三讲 字符串处理

字符串处理函数 将格式化数据写入字符串: sprintf 字符串长度查询函数: strlen 字符串复制函数: strcpy 、 strncpy 字符串连接函数: strcat 字符串比较函数:

strcmp 、 strncmp 、 stricmp 、 strnicmp 字符串搜索函数:

strcspn 、 strspn 、 strstr 、 strtok 、 strchr 字符串大小写转换函数: strlwr 、 strupr这些函数都要求 #include <string.h>

Page 5: 程序设计实习 第三讲 字符串处理

把” hello world”复制到 str2

把 str2 复制到 str1

查询 str1 中字符串的长

字符串拷贝和求字符串长度char *strcpy(char *dest, const char *src);int strlen(const char *s);#include <stdio.h>#include <string.h>void main(){

char str1[10]="hello", str2[12];strcpy(str2,"hello world");

printf("length:%d(str1); %d(str2)\n", strlen(str1), strlen(str2));

strcpy(str1, str2);printf("length:%d(str1); %d(str2)\n",

strlen(str1), strlen(str2));printf("%s\n", str1);return;

}

Page 6: 程序设计实习 第三讲 字符串处理

输出结果:length:5(str1); 11(str2)

length:11(str1); 11(str2)

hello world str1 存储了 11 个非’ \0’ 字符?

strcpy 在复制字符串 str2 到 str1 时,不检查 str2是否超出了 str1 的存储容量,而是直接将 str2 中存储的字符串复制到从 str1 开始的一段连续区域

在程序中要特别注意这种情况所引发的程序运行 不确定性

Page 7: 程序设计实习 第三讲 字符串处理

h e l l o \0 str1

str2

main()

h e l l o \0 str1

h e l l o w o r l d \0 str2

strcpy(str2,“hello world");

h e l l o str1

h e l l o w o r l d \0 str2

strcpy(str1,str2);

w o r l d \0

Page 8: 程序设计实习 第三讲 字符串处理

用 strlen时常犯的错误

int MyStrchr(char * s, char c) // 看 s 中是否包含 c

{

for( int i = 0; i < strlen(s) -1 ; i ++ )

if( s[i] == c)

return 1;

return 0;

} 哪里不好?这个函数执行时间和 s 的长度是什么关系?

strlen 是一个 o(N) 的函数,每次判断 i < strlen(s) – 1 都要执行,太浪费时间了

Page 9: 程序设计实习 第三讲 字符串处理

#include <stdio.h>

#include <string.h>

void main()

{

char str1[100]="hello", str2[10]="^_^";

strcat(str1," world ");

printf("%s\n", str1);

strcat(str1, str2);

printf("%s\n", str1);

return;

}

把” world” 添加到str1 中原字符串的末

字符串添加 strcat

把 str2 中的字符串添加到 str1 中原字符串的末

char *strcat(char *dest, const char *src);把 src 内容加到 dest 后面,同样不会考虑 dest 是否够长

输出:hello worldhello world ^_^

Page 10: 程序设计实习 第三讲 字符串处理

字符串比较函数 strcmp(分大小写)、 stricmp(不分大小写)int strcmp(const char *s1, const char *s2);

int stricmp(const char *s1, const char *s2);

Return ValueIf s1 is... return value is...less than s2 < 0the same as s2 == 0greater than s2 > 0

Compares one string to another, without case sensitivity.

Return ValueIf s1 is... return value is...less than s2 < 0the same as s2 == 0greater than s2 > 0

Page 11: 程序设计实习 第三讲 字符串处理

关于 stricmp 的注意事项

stricmp 不是严格意义上的 C++ 标准库函数,所以不同编译器的实现有所不同。

比如在 POJ 上交题,编译器选 C++ 时,该函数应写为:

_stricmp

不同编译器的 _stricmp 库函数执行过程也可能不一样,有的是把小写转换成大写后比较,有的是把大写转换成小写后比较,所以,在待比较字符串里面有非字母字符,比如标点符号时,不同编译器的 _stricmp 执行的结果有可能不同

Page 12: 程序设计实习 第三讲 字符串处理

字符串比较函数 strcmp 、 stricmp

#include <string.h>#include <stdio.h>char string1[] = "The quick brown dog jumps over the lazy fox";char string2[] = "The QUICK brown dog jumps over the lazy

fox";void main( void ){ int result; printf( "Compare strings:\n\t%s\n\t%s\n\n", string1, string2 ); result = strcmp( string1, string2 ); printf( "strcmp: result=%d\n", result); result = stricmp( string1, string2 ); printf( "stricmp: result=%d\n", result); return;}

Page 13: 程序设计实习 第三讲 字符串处理

输出:

Compare strings: The quick brown dog jumps over the lazy fox The QUICK brown dog jumps over the lazy fox

strcmp: result=1stricmp: result=0

Page 14: 程序设计实习 第三讲 字符串处理

查找子串 strstr

char *strstr(char *s1, char *s2);

Scans a string for the occurrence of a given substring.查找给定字符串在字符串中第一次出现的位置,返回位置指针strstr scans s1 for the first occurrence of the substring s2.

Return Value

strstr returns a pointer to the element in s1, where s2 begins (points to s2 in s1). If s2 does not occur in s1, strstr returns null.

如果找到,返回指针,指向 s1 中第一次出现 s2 的位置如果找不到,返回 NULL

Page 15: 程序设计实习 第三讲 字符串处理

查找子串 strstr

#include <string.h>#include <stdio.h>char str[] = "lazy";char string[] = "The quick brown dog jumps over the lazy fox";void main( void ){ char *pdest; int result; pdest = strstr( string, str ); result = pdest - string + 1; if( pdest != NULL ) printf( "%s found at position %d\n\n", str, result ); else printf( "%s not found\n", str );

} 输出: lazy found at position 36

在 string 中搜索 str ,返回str 在 string 中第一次出现的

位置

Page 16: 程序设计实习 第三讲 字符串处理

在字符串中查找字符 strchrchar *strchr(char *s, int c);Scans a string for the first occurrence of a given character.查找给定字符在字符串中第一次出现的位置,返回位置指针strchr scans a string in the forward direction, looking for a specific character. strchr finds the first occurrence of the character c in the string s.

Return Value

strchr returns a pointer to the first occurrence of the character c in s; if c does not occur in s, strchr returns null.

Page 17: 程序设计实习 第三讲 字符串处理

在字符串中查找字符 strchr#include <string.h>

#include <stdio.h>

int ch = 'r';

char string[] = "The quick brown dog jumps over the lazy fox";

void main( void )

{

char *pdest;

int result;

pdest = strchr( string, ch );

result = pdest - string + 1;

if( pdest != NULL )

printf( "Result:\tfirst %c found at position %d\n\n", ch, result );

else

printf( "Result:\t%c not found\n" );

} 输出: Result: first r found at position 12

在 string 中搜索 ch ,返回 str在 string 中第一次出现的位置

Page 18: 程序设计实习 第三讲 字符串处理

字符串部分拷贝 strncpy

char *strncpy(char *dest, char *src, int maxlen);

将前 maxlen 个字符从 src 拷贝到 dest

1 )如果 src 中字符不足 maxlen 个,则连’ \0’ 一起拷贝,’ \0’ 后面的不拷贝

2) 如果 src 中字符大于等于 maxlen 个,则拷贝 maxlen 个字符

Page 19: 程序设计实习 第三讲 字符串处理

#include <iostream>

using namespace std;

#include <stdio.h>

int main(void)

{

char s1[20] = "1234567890";

char s2[] = "abcd" ;

strncpy( s1,s2,5);

cout << s1 << endl;

strcpy( s1,"1234567890");

strncpy( s1,s2,4);

cout << s1 << endl;

return ;

}

字符串部分拷贝 strncpy

输出:abcd

abcd567890

Page 20: 程序设计实习 第三讲 字符串处理

数组作为函数的参数#include <iostream>using namespace std;char str1[200] = "Hello,World"; char str2[100] = "Computer";void swap( char s1[ ], char * s2) // 交换两个字符串的内容{

char c;for( int i = 0; s1[i] || s2[i]; i ++ ){ // ‘\0’ 的 Ascii 码就是 0

c = s2[i];s2[i] = s1[i];s1[i] = c;

}s1[i+1] = s2[i+1] = 0;

}int main(){

swap(str1,str2);cout << str1 << endl << str2;return 0;

}

输出:ComputerHello,World

Page 21: 程序设计实习 第三讲 字符串处理

例题: ai2767 Caesar密码 (P115)

问题描述 Julius Caesar 生活在充满危险和阴谋的年代。为

了生存,他首次发明了密码,用于军队的消息传递 假设你是 Caesar 军团中的一名军官,需要把

Caesar 发送的消息破译出来、并提供给你的将军。消息加密的办法是:对消息原文中的每个字母,分别用该字母之后的第 5 个字母替换(例如:消息原文中的每个字母 A 都分别替换成字母 F , V 替换成 A,W 替换成 B… ),其他字符不 变,并且消息原文的所有字母都是大写的。

Page 22: 程序设计实习 第三讲 字符串处理

输入 最多不超过 100 个数据集组成。每个数据集由 3 部分组成

起始行: START 密码消息:由 1 到 200 个字符组成一行,表示

Caesar 发出的一条消息 结束行: END

在最后一个数据集之后,是另一行: ENDOFINPUT 输出

每个数据集对应一行,是 Caesar 的原始消息。

密码字母: A B C D E F G H I J K L M N O P Q R S T U V W X Y Z

原文字母: V W X Y Z A B C D E F G H I J K L M N O P Q R S T U

Page 23: 程序设计实习 第三讲 字符串处理

样例输入START NS BFW, JAJSYX TK NRUTWYFSHJ FWJ YMJ WJXZQY TK YWNANFQ HFZXJX END START N BTZQI WFYMJW GJ KNWXY NS F QNYYQJ NGJWNFS ANQQFLJ YMFS XJHTSI

NS WTRJ END START IFSLJW PSTBX KZQQ BJQQ YMFY HFJXFW NX RTWJ IFSLJWTZX YMFS MJ END ENDOFINPUT

样例输出IN WAR, EVENTS OF IMPORTANCE ARE THE RESULT OF TRIVIAL CAUSES

I WOULD RATHER BE FIRST IN A LITTLE IBERIAN VILLAGE THAN SECOND IN ROME

DANGER KNOWS FULL WELL THAT CAESAR IS MORE DANGEROUS THAN HE

Page 24: 程序设计实习 第三讲 字符串处理

#include <iostream>#include <string.h>using namespace std;int main() {

char szLine[300];while( cin.getline(szLine,210) ) { // 可用此方式判断数据是否读完

/*cin.getline 读取一行,第一个参数是缓冲区地址;第二个参数是缓冲区大小,为了防止越界用的。缓冲区不够大,就自动截断。它会自动往缓冲区末尾添加 ‘ \0’ 。 */

if( strcmp( szLine,"ENDOFINPUT") == 0) break;cin.getline(szLine,210); // 读取密文for( int i = 0; szLine[i]; i ++ )

if( szLine[i] >= 'A' && szLine[i] <= 'Z' ) {szLine[i] -= 5;if( szLine[i] < 'A' )

szLine[i] = 'Z' - ('A' - szLine[i]) + 1;}

cout << szLine; cout << endl;cin.getline(szLine,210); // 读取 END

} return 0;}

Page 25: 程序设计实习 第三讲 字符串处理

输入若干行单词(不含空格),请按字典序排序输出。大小写有区别。单词一共不超过 100 行,每个单词不超过 20 字符

Sample input:WhatmanTellAboutback

例题:单词排序

Sample output:

About

Tell

What

back

man

Page 26: 程序设计实习 第三讲 字符串处理

用什么保存多个单词?例题:单词排序

答:用二维字符数组

char Word[100][30];则表达式 Word[i] 的类型就是 char *Word[i] 就是数组中的一行,就是一个字符串Word[i][0] 就是 Word[i] 这个字符串的头一个字符

二维字符数组也能初始化,例如:char week[7][10]={"Saturday", "Sunday",

"Monday", "Tuesday", "Wednesday", "Thursday", "Friday"};

Page 27: 程序设计实习 第三讲 字符串处理

#include <stdlib.h>#include <stdio.h>#include <string.h>char Word[100][30];int MyCompare( const void * e1, const void * e2 ){

return strcmp( (char * ) e1, (char * ) e2 );}int main() {

int n = 0; // 单词个数while(scanf("%s",Word[n]) != EOF && Word[n][0])

n ++; qsort(Word, n,sizeof(Word[0]),MyCompare);for( int i = 0; i < n; i ++ )

printf(“%s\n”,Word[i]); // ‘\n’ 表示换行return 0;

}

例题:单词排序

为了对付有可能最后一行读入的是空行

Page 28: 程序设计实习 第三讲 字符串处理

例题: ai2744 子串 (P112)

问题描述:给出一些由英文字符组成的大小写敏感的字符串的集合 s ,请找到一个最长的字符串 x ,使得对于 s 中任意字符串 y , x或者是 y 的子串,或者 x 中的字符反序之后得到的新字符串是 y 的子串。

输入:输入的第一行是一个整数 t (1 <= t <= 10) , t 表示测试数据的数目。对于每一组测试数据,第一行是一个整数 n (1 <= n <= 100) ,表示已经给出 n 个字符串。接下来 n 行,每行给出一个长度在 1 和 100 之间的字符串。

Page 29: 程序设计实习 第三讲 字符串处理

输出:对于每一组测试数据,输出一行,给出题目中要求的字符串 x 的长度。

Sample Input:23ABCDBCDFFBRCD2rose orchid

例题: ai2744 子串 (P112)

Sample Output22

Page 30: 程序设计实习 第三讲 字符串处理

思路:

随便拿出输入数据中的一个字符串,从长到短找出它的所有子串,直到找到否符合题目要求的。

例题: ai2744 子串 (P112)

改进:

不要随便拿,要拿输入数据中最短的那个。从长到短找出它的所有子串,直到找到否符合题目要求的。

Page 31: 程序设计实习 第三讲 字符串处理

#include <stdio.h>#include <string.h>int t, n; char str[100][101];int searchMaxSubString(char* source);int main() {

int i, minStrLen, subStrLen;char minStr[101];scanf("%d", &t);while(t--) {

scanf("%d", &n);minStrLen = 100; //记录输入数据中最短字符串的长度for (i = 0; i < n; i++) {// 输入一组字符串

scanf("%s", str[i]);if ( strlen(str[i]) < minStrLen ) {// 找其中最短字符串

strcpy(minStr, str[i]);minStrLen = strlen(minStr);

}}subStrLen = searchMaxSubString(minStr);// 找答案printf("%d\n", subStrLen);

} return 0;}

Page 32: 程序设计实习 第三讲 字符串处理

_strrev (char * s) 将字符串 s 颠倒

int searchMaxSubString(char* source) {int subStrLen = strlen(source), sourceStrLen = strlen(source);int i, j; bool foundMaxSubStr;char subStr[101], revSubStr[101];while ( subStrLen > 0 ) {// 搜索不同长度的子串,从最长的子串开始搜索

for (i = 0; i <= sourceStrLen - subStrLen; i++) { // 搜索长度为 subStrLen 的全部子串

strncpy(subStr, source+i, subStrLen);strncpy(revSubStr, source+i, subStrLen);subStr[subStrLen] = revSubStr[subStrLen] = '\0'; _strrev(revSubStr); foundMaxSubStr = true;for( j = 0; j < n; j++) //遍历所有输入的字符串 if ( strstr(str[j], subStr) == NULL &&

strstr(str[j], revSubStr) == NULL ) {foundMaxSubStr = false;break;

}if (foundMaxSubStr) return subStrLen;

}subStrLen--;

} return(0);

}

Page 33: 程序设计实习 第三讲 字符串处理

例题: POJ1936 All in All

问题描述:给定两个字符串 s 和 t ,请判断 s是否是 t 的子序列。即从 t 中删除一些字符,将剩余的字符连接起来,即可获得 s 。

输入:包括若干个测试数据。每个测试数据由两个 ASCII 码的数字和字母串 s 和 t 组成, s和 t 的长度不超过 100000 。

输出:对每个测试数据,如果 s 是 t 的子序列则输出“ Yes” ,否则输出“ No” 。

Page 34: 程序设计实习 第三讲 字符串处理

样例输入sequence subsequence

person compression

VERDI vivaVittorioEmanueleReDiItalia

caseDoesMatter CaseDoesMatter 样例输出

Yes

No

Yes

No

例题: POJ1936 All in All

Page 35: 程序设计实习 第三讲 字符串处理

例题: POJ1936 All in All思路:

关键是看 s 中的每个字符是否在 t 中出现,而且顺序一致。

设两个指针,分别指向 s 和 t 的开头,然后,如果 t 的指针指向的字符和 s 的指针指向的字符相同,则将 s 的指针向前移动一个字符。 t 的指针则不停向前每次移动一个字符,

直到 s 指针指向末尾的 ‘ \0’ 说明 Yes 找到或 t 的指针指向末尾的 ‘ \0’ ,说明 No

Page 36: 程序设计实习 第三讲 字符串处理

#include <stdio.h>char s[100010];char t[100010];int main(){

int i,j;while (scanf( "%s%s",s,t) > 0 ) {

for( i = 0,j = 0 ; s[i] && t[j]; j ++ ) {if( t[j] == s[i] )

i ++;}if ( s[i] == 0) // ‘\0’ 的 Ascii 码就是 0

printf("Yes\n");else

printf("No\n");}return 0;

}

Page 37: 程序设计实习 第三讲 字符串处理

ai2804 词典作业提示:为加快查找速度,可能会用到折半查找库函数 :

该函数与 qsort 有类似之处void *bsearch(const void *key, const void *base,

size_t nelem, size_t width,

int ( *fcmp)(const void *, const void *));

该函数在一个排过序的数组 base 里查找值和 * key 一样大 (未必是严格相等,只是按 fcmp 指定

的比较规则是一样大的而已) 的元素。找到的话,返回第一个该种元素的地址,找不到的话,返回 NULL

复杂度: O(lgN) 要求比较大小的规则和排序的规则一样

Page 38: 程序设计实习 第三讲 字符串处理

ai2804 词典作业提示:

怎么判断读到空行?

char s[100];

可以用 gets(s) 一次读取一行,然后再用sscanf 从 s 中分出英文和外文

gets(s) 读到空行时 ,s 的长度为 0 ,即 s[0] = 0

读到文件尾巴时, gets 返回 NULL

scanf 返回 EOF也可以说明到了文件尾巴注意,说不定文件尾有空行

Page 39: 程序设计实习 第三讲 字符串处理

ai2804 词典作业提示:

数据量很大,注意用 cin, cout说不定会超时

Page 40: 程序设计实习 第三讲 字符串处理

作业 POJ2734 十进制 -> 八进制 POJ2797 最短前缀 POJ2804 词典