USACO Section 1.3 PROB Calf Flac(最長迴文子串)

題目原文

首先還是貼原文:

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)

11
Madam, 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';
			else
				ss += 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: calfflac
LANG: 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';
			else
				ss += 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: calfflac
LANG: C++

Compiling...
Compile: OK

Executing...
   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;

void
findpal(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;
	}
    }
}

void
main(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


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章