Programming Forums
User Name Password Register
 

RSS Feed
FORUM INDEX | TODAY'S POSTS | UNANSWERED THREADS | ADVANCED SEARCH

Reply
 
Thread Tools Display Modes
Old Jun 12th, 2006, 7:03 PM   #1
DaWei
Resident Grouch
 
DaWei's Avatar
 
Join Date: Jun 2005
Posts: 6,453
Rep Power: 10 DaWei is on a distinguished road
Simple, contrived programming exercise

Most of us know how to test a word (or phrase) to see if it's a palindrome. This exercise is, therefore, somewhat artificial. Consider a phrase written on the side of a flexible material. Presuming that you can visualize characters upside down, you may locate palindromes by bending the material back on itself and sliding it along, looking for matches. Duplicate this method, using any language you like. Use a greedy search. That is, find the longest palindrome present at any given point. No need to find a short palindrome located within a longer one. No need to detect a palindrome that overlaps another. Most famous palindromes ignore spaces and punctuation (including apostrophes and quotation marks). That will be the case here. When you have discovered the first, longest palindrome, substitute for it the character sequence, **PALINDROME**, and proceed with the remainder of the phrase.

1. Show your 'stripped' material (less punctuation, etc.).
2. Output your first palindrome.
3. Output the remainder.
4. Do it all again with the remainder.
5. Output the substituted phrase.

Here are a couple of test phrases:

Makeable was I ere I saw Elba from the racecar (Madam, I'm Adam).
"Am I mad, eh?" Giselle sighed, "Am I, Ma?"

Critique each other's code, offering suggestions for improvement. Be big boys, don't get pissed at criticism.
__________________
Abstraction doesn't make it impossible to write bad code; it makes it possible to write superior code.
Contributor's Corner: Grumpy on C++ Exceptions DaWei on Pointers
DaWei is offline   Reply With Quote
Old Jun 12th, 2006, 9:08 PM   #2
Jimbo
Battle Programmer
 
Jimbo's Avatar
 
Join Date: Feb 2006
Location: Bellevue, WA, USA
Posts: 773
Rep Power: 3 Jimbo is on a distinguished road
Does the "do it all again" in #4 mean to do steps 1-3, as though recursive? I.e. on your first sample, it would result in
1) MakeablewasIereIsawElbafromtheracecarMadamImAdam
2) ablewasiereisawelba
3) MakefromtheracecarMadamImAdam
4) 1) MakefromtheracecarMadamImAdam
   2) madamimadam
   3) makefromtheracecar
   4) 1)makefromtheracecar
      2) racecar
      ... (didn't want to bother with this, and I think the example will suffice)
      5) makefromthe**PALINDROME**
   5) makefromthe**PALINDROME****PALINDROME**
5) make**PALINDROME**from the**PALINDROME****PALINDROME**
or just output replace the longest? If neither, could you post a sample solution to the first test phrase? And if you do mean for it to recurse, do we consider palindromes of length 1?

I dunno if I even worked things out right, I was just doing them quickly here to illustrate my question.
Jimbo is offline   Reply With Quote
Old Jun 12th, 2006, 9:29 PM   #3
DaWei
Resident Grouch
 
DaWei's Avatar
 
Join Date: Jun 2005
Posts: 6,453
Rep Power: 10 DaWei is on a distinguished road
No, minimum palindrome of length three. I didn't mean to imply recursion, but to find the first, longest palindrome, get rid of that part of the phrase, and find the next.

The intent of the problem was to force development of a contrived solution (the folded phrase) despite the fact that it's not the most effective method. This provides a 'game' programming exercise that should result in some thought and considerations, as opposed to the 'add a line' thing. One aspect of creativity in developing solutions is to consider as many methods as possible, rejecting the worst. One may discover a new method. Also, sometimes one is forced to solve a problem as the client wishes it solved, not in the best possible way. Just an attempt to fill a seeming void.

"Am I mad, eh?" Giselle sighed, "Am I, Ma?"

The 'drome after 17 invalid characters removed:
AmImadehGisellesighedAmIMa

First palindrome: AmIma
Remainder: dehGisellesighedAmIMa

Next palindrome: dehGisellesighed
Remainder: AmIMa

Next palindrome: AmIMa

Substituted result: **PALINDROME****PALINDROME****PALINDROME**

Notice that this isn't truly the greediest, as the entire phrase is a palindrome. I don't really know how to explain it, and won't be picky at the solutions. I want to see the fold method implemented, simply because it's a humanistic solution forced on the machine, as opposed to a von Neumann solution forced on the coder.
__________________
Abstraction doesn't make it impossible to write bad code; it makes it possible to write superior code.
Contributor's Corner: Grumpy on C++ Exceptions DaWei on Pointers
DaWei is offline   Reply With Quote
Old Jun 13th, 2006, 8:16 AM   #4
DaWei
Resident Grouch
 
DaWei's Avatar
 
Join Date: Jun 2005
Posts: 6,453
Rep Power: 10 DaWei is on a distinguished road
Okay, this is what I mean to say. Find the first palindrome of any length (minimum 3 to qualify). No need to find a palindrome within or overlapping. This is taken care of by discarding the found palindrome (and by substituting in the **PALINDROME** in the final output string) and working with the remainder. You new guys that are trying to learn to code, here's your chance to solve a problem on demand and to a specification, however silly the overall concept might be, and to express that solution in the language of your choice.
__________________
Abstraction doesn't make it impossible to write bad code; it makes it possible to write superior code.
Contributor's Corner: Grumpy on C++ Exceptions DaWei on Pointers
DaWei is offline   Reply With Quote
Old Jun 13th, 2006, 10:11 AM   #5
NSchnarr
Newbie
 
Join Date: May 2006
Posts: 28
Rep Power: 0 NSchnarr is on a distinguished road
Sounds fun, ill try to work this out when i get home from work.
NSchnarr is offline   Reply With Quote
Old Jun 13th, 2006, 3:10 PM   #6
titaniumdecoy
Programming Guru
 
titaniumdecoy's Avatar
 
Join Date: Nov 2005
Posts: 1,007
Rep Power: 5 titaniumdecoy will become famous soon enough
Send a message via AIM to titaniumdecoy
This was a surprisingly difficult challenge (at least, for me). My Python code is below:

"""Overlapping Palindrome Test"""

_debug = True

min_len = 1

### The phrase ###

phrase = '"Am I mad, eh?" Giselle sighed, "Am I, Ma?"';

if _debug: print 'The phrase:', phrase

### Strip the phrase ###

phrase = phrase.lower()

alphabet = 'abcdefghijklmnopqrstuvwxyz'
phrase = [letter for letter in phrase if letter in alphabet]

if _debug: print 'The stripped phrase:', ''.join(phrase)

if _debug: print # Newline (for aesthetics)

# Pop the last min_len characters from phrase into reversed
reversed = []
for i in range(min_len):
	reversed.append(phrase.pop())

results = []

# t holds odd letters (for example, 'i' in 'madamimadam')
t = None

for pos in range( len(phrase) ):
    
    # Try each match with and without t
    if t == None:
        t = phrase.pop()
    else:
        reversed.append(t)
        t = None
    
    phr_len = len(phrase)
    rev_len = len(reversed)
    
    # Store letters in temp as we accumulate a palindrome
    temp = ''
    
    for i in range(rev_len):
    
        # Attempt to match corresponding indices of phrase and reversed
        
        j = phr_len - rev_len + i
        
        if reversed[i] != phrase[j] and i != rev_len - 2: 
            # If unsuccessful, try again with new letters
            break
        else:
            # We have begun to accumulate a palindrome
            temp += reversed[i]
            
            if i == rev_len - 1:
                # We have a palindrome!
                
                # Assemble the palindrome
                palindrome = temp
                if t != None:
                    palindrome += t
                palindrome += temp[::-1]
                
                if _debug:
                    print 'Palindrome: ', palindrome
                    print 'Remainder: ', ''.join(phrase)
                    print
                
                # Add the palindrome to the list of results
                results.append(palindrome)

### Print the palindromes (if not in debug mode) ###

if not _debug:
    print "\n".join([result for result in results])
Output:

The phrase: "Am I mad, eh?" Giselle sighed, "Am I, Ma?"
The stripped phrase: amimadehgisellesighedamima

Palindrome:  amima
Remainder:  amimadehgisellesighedam

Palindrome:  amimadehgisellesighedamima
Remainder:  amimadehgisel
Suggestions?

Last edited by titaniumdecoy; Jun 13th, 2006 at 3:29 PM.
titaniumdecoy is offline   Reply With Quote
Old Jun 13th, 2006, 3:35 PM   #7
Jimbo
Battle Programmer
 
Jimbo's Avatar
 
Join Date: Feb 2006
Location: Bellevue, WA, USA
Posts: 773
Rep Power: 3 Jimbo is on a distinguished road
first thing I whipped together (in C++). It iterates through the input string (passed as argv[1]), and replaces the longest palindrome (if any) from whatever index it's at.
#include <string>
#include <cctype>
#include <iostream>

const int MINPALINDROMESIZE = 3;

std::string strip(const std::string& s)
{
  std::string stripped;
  for(int i = 0; i < s.length(); ++i)
    if(std::isalpha(s[i]))
      stripped += s[i];
  return stripped;
}

bool isPalindrome(const std::string& s)
{
  for(int i = 0, j = s.length() - 1; i < j; ++i, --j)
    if(std::tolower(s[i]) != std::tolower(s[j]))
      return false;
  return true;
}

std::string firstPalindrome(const std::string& s)
{
  int start = 0, len = 1;
  for(int i = 0; i < s.length(); ++i)
  {
    int curStart = i, curLen = 1;
    for(int k = s.length() - 1; i < k; --k)
    {
      if(std::tolower(s[i]) == std::tolower(s[k])) // ends match, so check insid
e
      {
        curLen = k - i + 1; // length of substring being considered
        if(curLen >= MINPALINDROMESIZE && isPalindrome(s.substr(i, curLen)))
        {
          if(curLen > len)
            start = curStart, len = curLen;
          goto end; // found largest pdrome for this starting letter
        }
      }
    }   
  }
end:
  std::string result;
  if(len >= MINPALINDROMESIZE)
    result = s.substr(start, len);
  return result;
}

int main(int argc, char** argv)
{
  if(argc != 2)
  {
    std::cerr << "Usage: " << argv[0] << " <phrase>" << std::endl;
    return 1;
  }
  std::string s = strip(argv[1]);
  std::cout << "The phrase after being stripped: " << std::endl;
  std::cout << s << std::endl << std::endl;
  std::string pal, res, rem;
  pal = firstPalindrome(s);
  if(pal == "")
    std::cout << "No palindromes found in this string.";
  else
  {
    rem = s.substr(s.find(pal)+pal.length());
    res = s.substr(0, s.find(pal)) + "**PALINDROME**";
    std::cout << "First Palindrome: " << pal << std::endl;
    if(rem != "")
      std::cout << "Remainder: " << rem << std::endl;
    while((pal = firstPalindrome(rem)) != "")
    {
      res += rem.substr(0, rem.find(pal)) + "**PALINDROME**";
      rem = rem.substr(rem.find(pal) + pal.length());
      std::cout << std::endl;
      std::cout << "Next Palindrome: " << pal << std::endl;
      if(rem != "")
        std::cout << "Remainder: " << rem << std::endl;
    }
    if(rem != "")
      res += rem;
    std::cout << std::endl;
    std::cout << "Substituted Result: " << res << std::endl;
  }
  return 0;
}

Sample output:
Quote:
jimmy@vera ~/projects/randomStuff $ ./a.out "Makeable was I ere I saw Elba from the racecar (Madam, I'm Adam)."
The phrase after being stripped:
MakeablewasIereIsawElbafromtheracecarMadamImAdam

First Palindrome: ablewasIereIsawElba
Remainder: fromtheracecarMadamImAdam

Next Palindrome: racecar
Remainder: MadamImAdam

Next Palindrome: MadamImAdam

Substituted Result: Make**PALINDROME**fromthe**PALINDROME****PALINDROME**
Quote:
jimmy@vera ~/projects/randomStuff $ ./a.out "\"Am I mad, eh?\" Giselle sighed, \"Am I, Ma?\""
The phrase after being stripped:
AmImadehGisellesighedAmIMa

First Palindrome: AmImadehGisellesighedAmIMa

Substituted Result: **PALINDROME**
I might write another version later to simply replace the first palindrome of length > MINPALINDROMELENGTH, so as to get output similar to what DaWei posted above... depends on how much free time I have :p

Last edited by Jimbo; Jun 13th, 2006 at 4:00 PM.
Jimbo is offline   Reply With Quote
Old Jun 13th, 2006, 4:13 PM   #8
Jessehk
The Oblivious One
 
Jessehk's Avatar
 
Join Date: May 2005
Location: Ontario, Canada
Posts: 655
Rep Power: 4 Jessehk is on a distinguished road
I have just finished my program in Ruby, but I am in the process of adding comments and such. I'm just posting so that you know there is interest, David.
__________________
Dr. Zoidberg: [ecstatic] I'm going to a movie... with FRIENDS!
Jessehk is offline   Reply With Quote
Old Jun 13th, 2006, 4:48 PM   #9
Jessehk
The Oblivious One
 
Jessehk's Avatar
 
Join Date: May 2005
Location: Ontario, Canada
Posts: 655
Rep Power: 4 Jessehk is on a distinguished road
Ok, here goes.

#Set this to true for a better understanding of how the program works
$DEBUG = false

# Return the largest palindrome anywhere in the string.
# It is assumed that the input has been passed first through process_string to
# rid it of only alphabetic characters.
# nil is returned if there are no palindromes in the string.
def find_largest_palindrome(raw_str)
	puts "Raw string = #{raw_str.join}"
    
    # an array to hold all the palindromes in the string
    pals = Array.new

    # loop through the indexes of the character array 
    raw_str.each_index do |a|
        # set the string to be tested for palindromes one letter less each time
	testing_str = raw_str[a..raw_str.length]
        
        if $DEBUG
            puts "Testing String: #{testing_str.join}"
            sleep(0.1)
        end

	0.upto(testing_str.length) do |b|
        # set the reversed string to the reverse of the testing string, but
        # cycle through all the possible reverses to find all the possible
        # matches.
	    reversed_str = testing_str.reverse[b..testing_str.length]
            
            if $DEBUG
                puts "\tReversed String: #{reversed_str.join}"
            end
            
	    sub_str = String.new
				
	    testing_str.each_index do |j|
		if testing_str[j] == reversed_str[j]
		    sub_str << testing_str[j]
		else
		    break
		end
            end
            
            unless sub_str.empty? or pals.include?(sub_str) or sub_str.length < 3
            	pals << sub_str
          
                if $DEBUG
                    puts "\t\t\t\tAdding palindrome: * #{sub_str} *"
                    sleep(1)
                end
            end 
        end
    end
    
    return pals.max { |a, b| a.length <=> b.length }
end

# Process the input string.
# First, all instances of **PALINDROME** are removed
# so they are not included in the search for palindromes.
# Second, all characters are removed except for letters of the  alphabet.
# An Array containing all the letters of the string is returned. 
def process_string(string)
	string.chomp.gsub("**PALINDROME**", '').gsub(/[^a-z]/i, '').downcase.split(//)
end

def dawei_challenge(phrase)
    result = process_string(phrase).join
	
    loop do
	pal = find_largest_palindrome(process_string(result))
	puts "Largest palindrome = #{pal or "None"}"
		
        if $DEBUG
            sleep(2)
        end
		
        if not pal
	    puts "No more palindromes!"
	    puts "Final result is \"#{result}\"."
	    return
	else
	    result.sub!(pal, '**PALINDROME**')
	    puts "Result so far: #{result}\n\n"
	end
    end
    
    result
end

text1 = "Makeable was I ere I saw Elba from the racecar (Madam, I'm Adam)."
text2 = "\"Am I mad, eh?\" Giselle sighed, \"Am I, Ma?\""
dawei_challenge(text1)

EDIT:Helps if I post the code :p

This is very hard for me to explain, so I recommend setting $DEBUG to true to get a better sense of this program.

A string is first processed, and then passed to find_largest_palindrome() repeatedly, each time substituting the largest one out.

find_largest_palindrome() took a lot of trial and error to write. Basically, it will go through all possible combinations of strings, and go through all possible strings to test it against.

Here is sample output if somebody does not have Ruby on their home computer:

$ ruby palindrome.rb
Raw string = makeablewasiereisawelbafromtheracecarmadamimadam
Largest palindrome = ablewasiereisawelba
Result so far: make**PALINDROME**fromtheracecarmadamimadam

Raw string = makefromtheracecarmadamimadam
Largest palindrome = madamimadam
Result so far: make**PALINDROME**fromtheracecar**PALINDROME**

Raw string = makefromtheracecar
Largest palindrome = racecar
Result so far: make**PALINDROME**fromthe**PALINDROME****PALINDROME**

Raw string = makefromthe
Largest palindrome = None
No more palindromes!
Final result is "make**PALINDROME**fromthe**PALINDROME****PALINDROME**".

My next challenge would be to attempt this in C or C++, but it was challenging enough for me using a scripting language! :p
__________________
Dr. Zoidberg: [ecstatic] I'm going to a movie... with FRIENDS!

Last edited by Jessehk; Jun 13th, 2006 at 5:12 PM.
Jessehk is offline   Reply With Quote
Old Jun 13th, 2006, 5:49 PM   #10
Arevos
Programming Guru
 
Arevos's Avatar
 
Join Date: Aug 2005
Location: England
Posts: 1,499
Rep Power: 5 Arevos is on a distinguished road
I took a somewhat functional approach:
import string
import sys

def parts(text, size):
	for i in xrange(len(text) - size + 1):
		yield text[i:i + size]
		
def substrings(text, min):
	for size in xrange(min, len(text) + 1):
		for part in parts(text, size):
			yield part

def maxlen(x, y):
	if len(x) > len(y): return x
	else: return y

def reverse(text):
	return "".join(list(reversed(text)))

def palindrones(text, min = 3):
	return set(s for s in substrings(text, min) if s == reverse(s))

def keep_only(text, keep):
	return "".join(c for c in text if c in keep)

def sanitise(text):
	return keep_only(text.lower(), string.lowercase)

def max_palindrone(text, min = 3):
	found = palindrones(sanitise(text), min)
	if found: return reduce(maxlen, found)

def split_at_substring(text, s):
	index = text.find(s)
	return text[:index], text[index:index + len(s)], text[index + len(s):]
	
def mark_palindrones(text, min = 3):
	palindrone = max_palindrone(text, min)
	if palindrone:
		before, _, after = split_at_substring(sanitise(text), palindrone)
		return mark_palindrones(before, min) + "**PALINDROME**" + \
		       mark_palindrones(after, min)
	else:
		return text

print mark_palindrones(sys.argv[1])
Output:
$ python palindrome.py "Makeable was I ere I saw Elba from the racecar (Madam, I'm Adam)."
make**PALINDROME**fromthe**PALINDROME****PALINDROME**

$ python palindrome.py '"Am I mad, eh?" Giselle sighed, "Am I, Ma?"'
**PALINDROME**
I considered creating a sanitised_find function that would allow me to retain the spaces and casing of the original string. But that seemed like too much hard work, and the problem was essentially solved, so I decided against further complexity.
Arevos is offline   Reply With Quote
Reply

Bookmarks

« Previous Thread in Forum | Next Thread in Forum »

Currently Active Users Viewing This Thread: 1 (0 members and 1 guests)
 
Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Forum Jump




DaniWeb IT Discussion Community
All times are GMT -5. The time now is 7:57 PM.

Powered by vBulletin® Version 3.7.0, Copyright ©2000 - 2009, Jelsoft Enterprises Ltd.
Copyright ©2007 DaniWeb® LLC