USACO Section 1.3 PROB Calf Flac(最长回文子串)

来源:互联网 发布:中国网络什么时候墙的 编辑:程序博客网 时间:2024/04/30 16:47

题目原文

首先还是贴原文:

Calf Flac

It is said that if you give an infinite number of cows an infinite number of heavy-duty laptops (with very large keys), that they will ultimately produce all the world's great palindromes. Your job will be to detect these bovine beauties.

Ignore punctuation, whitespace, numbers, and case when testing for palindromes, but keep these extra characters around so that you can print them out as the answer; just consider the letters `A-Z' and `a-z'.

Find the largest palindrome in a string no more than 20,000 characters long. The largest palindrome is guaranteed to be at most 2,000 characters long before whitespace and punctuation are removed.

PROGRAM NAME: calfflac

INPUT FORMAT

A file with no more than 20,000 characters. The file has one or more lines which, when taken together, represent one long string. No line is longer than 80 characters (not counting the newline at the end).

SAMPLE INPUT (file calfflac.in)

Confucius say: Madam, I'm Adam.

OUTPUT FORMAT

The first line of the output should be the length of the longest palindrome found. The next line or lines should be the actual text of the palindrome (without any surrounding white space or punctuation but with all other characters) printed on a line (or more than one line if newlines are included in the palindromic text). If there are multiple palindromes of longest length, output the one that appears first.

SAMPLE OUTPUT (file calfflac.out)

11Madam, I'm Adam


分析

这道题目是一道最长回文子串问题,最长回文子串问题是一个很经典的问题。不过由于原文题的输入数据中存在空格、换行、标点等特殊字符,需要先剔除才能开始寻找最长回文子串,另外判断回文的时候还要忽略大小写,最后输出的时候要求把剔除掉的标点和空格都要重新加到字符串中。

剔除空格、换行、标点的操作比较简单,通过字符判断一下就可以了,解决这道题的时候可以首先剔除空格、换行和标点,也可以在判断最长回文的时候忽略空格和标点,这里本文采用了前者,主要是在判断中忽略特殊字符的逻辑相对复杂,不如先求好回文再还原;


接下来讲如何在一段字符串中求取最长回文子串,求最长回文子串是一个动态规划问题,但是本文不从数学的角度出发来讲,而是直接叙述整个求取过程。

回文的意思即一个字符串顺序和倒序是相同的,例如,aba是一个回文。一般人最初判断回文的想法是首字符与最后一个字符判断,然后第二个字符与倒数第二个判断,一直判断到字符串中间,然而这样忽略了回文字符串的一个重要特性,即假设一个长度为3的字符串***不回文的话,包含着的其他任意字符串XX***XX都是不回文的,因此判断回文的时候,需要反过来,先从字符串的中间开始判断,这样可以减少很多的判断量;但是,这样的判断方法仍然还有一个问题,就是如果一个字符串的所有字符相同的时候,例如“aaaaaaaa”,这显然是一个回文字符串,依据之前的判断方法,不管是从中间开始判断还是从首尾开始判断需要的判断次数都是一样的,而实际上,判断一个字符串是否是回文的时候,连续的相同的字符是可以视为一个字符的,例如“baaaaaaab”在判断的时候可以认为是“bab”,因此在判断的时候,首先要判断是否有连续相同的字符串。

因此,给定一个字符串,寻找最长回文子串的流程是:


依次以字符串的每一个元素作为判断起点;

搜索连续相同的字符串,跳过;

向两侧搜索判断知道发现不相同的字符,记录当前得到的回文子串及其长度

循环上述过程,最终找到输入字符串的最长回文子串。



代码实现

下面分段贴代码。


首先实现一个函数判断是否为特殊字符

bool isAlpha(char &c){return (c >= 'a' && c <= 'z')||(c >= 'A' && c <= 'Z');}


在输入数据之后首先进行大小写转换和特殊字符剔除,如下:

for (int i=0;i!=str.length();i++){if((str[i] >= 'a' && str[i] <= 'z')||(str[i] >= 'A' && str[i] <= 'Z')){if((str[i] >= 'A' && str[i] <= 'Z'))ss += str[i] - 'A' + 'a';elsess += str[i];}}


进行最长回文子串判断:

int max=0;int len=0;int startpoint=0;for (int i=0;i!=ss.length();i++){int bkwd=i;int fwd = i;while(ss[fwd+1] == ss[bkwd]){fwd++;}i = fwd;while(bkwd>=1 && fwd < ss.length() && ss[bkwd-1] == ss[fwd+1]){bkwd--;fwd++;}len = fwd - bkwd + 1;if(len > max){startpoint = bkwd;max = len;}}

最后还原输出:

int count = 0;for (int i=0;i!=str.length();i++){if(isAlpha(str[i])){count++;if(count-1 == startpoint){startpoint = i;break;}}}string out = "";count = 1;for (int i= startpoint;count <= max ;i++){out += str[i];if(isAlpha(str[i]))count++;}fout << out << endl;

完整的提交代码

/*ID: PROG: calfflacLANG: C++*/#include <fstream>#include <algorithm>#include <vector>#include <string>#include <math.h>#include <map>#include <iostream>using namespace std;bool isAlpha(char &c){return (c >= 'a' && c <= 'z')||(c >= 'A' && c <= 'Z');}int main(){ifstream fin("calfflac.in");ofstream fout ("calfflac.out");string tmp;string str="";getline(fin,tmp);str += tmp;while(getline(fin,tmp)){str += "\n";str += tmp;}string ss = "";for (int i=0;i!=str.length();i++){if((str[i] >= 'a' && str[i] <= 'z')||(str[i] >= 'A' && str[i] <= 'Z')){if((str[i] >= 'A' && str[i] <= 'Z'))ss += str[i] - 'A' + 'a';elsess += str[i];}}int max=0;int len=0;int startpoint=0;for (int i=0;i!=ss.length();i++){int bkwd=i;int fwd = i;while(ss[fwd+1] == ss[bkwd]){fwd++;}i = fwd;while(bkwd>=1 && fwd < ss.length() && ss[bkwd-1] == ss[fwd+1]){bkwd--;fwd++;}len = fwd - bkwd + 1;if(len > max){startpoint = bkwd;max = len;}}fout << max << endl;int count = 0;for (int i=0;i!=str.length();i++){if(isAlpha(str[i])){count++;if(count-1 == startpoint){startpoint = i;break;}}}string out = "";count = 1;for (int i= startpoint;count <= max ;i++){out += str[i];if(isAlpha(str[i]))count++;}fout << out << endl;return 0;}


提交结果

TASK: calfflacLANG: C++Compiling...Compile: OKExecuting...   Test 1: TEST OK [0.005 secs, 3500 KB]   Test 2: TEST OK [0.003 secs, 3500 KB]   Test 3: TEST OK [0.000 secs, 3500 KB]   Test 4: TEST OK [0.000 secs, 3500 KB]   Test 5: TEST OK [0.000 secs, 3500 KB]   Test 6: TEST OK [0.008 secs, 3500 KB]   Test 7: TEST OK [0.008 secs, 3500 KB]   Test 8: TEST OK [0.011 secs, 3500 KB]All tests OK.

官方参考答案:

#include <stdio.h>#include <stdlib.h>#include <string.h>#include <assert.h>#include <ctype.h>char fulltext[21000];char text[21000];char *pal;int pallen;voidfindpal(void){    char *p, *fwd, *bkwd, *etext;    int len;    etext = text+strlen(text);    for(p=text; *p; p++) {/* try palindrome with *p as center character */for(fwd=bkwd=p; bkwd >= text && fwd < etext && *fwd == *bkwd;fwd++, bkwd--);bkwd++;len = fwd - bkwd;if(len > pallen) {    pal = bkwd;    pallen = len;}/* try palindrome with *p as left middle character */for(bkwd=p, fwd=p+1;          bkwd >= text && fwd < etext && *fwd == *bkwd; fwd++, bkwd--);bkwd++;len = fwd - bkwd;if(len > pallen) {    pal = bkwd;    pallen = len;}    }}voidmain(void){    FILE *fin, *fout;    char *p, *q;    int c, i, n;    fin = fopen("calfflac.in", "r");    fout = fopen("calfflac.out", "w");    assert(fin != NULL && fout != NULL);    /* fill fulltext with input, text with just the letters */    p=fulltext;    q=text;    while((c = getc(fin)) != EOF) {if(isalpha(c))    *q++ = tolower(c);*p++ = c;    }    *p = '\0';    *q = '\0';    findpal();    fprintf(fout, "%d\n", pallen);    /* find the string we found in the original text       by finding the nth character */n = pal - text;    for(i=0, p=fulltext; *p; p++)if(isalpha(*p))    if(i++ == n)break;    assert(*p != '\0');    /* print out the next pallen characters */    for(i=0; i<pallen && *p; p++) {fputc(*p, fout);if(isalpha(*p))    i++;    }    fprintf(fout, "\n");    exit(0);}

THE END


0 0
原创粉丝点击