Downfall - "Droppin bombs since '04"

  • home
  • forums
  • calendar
  • gallery
  • handbook
  • raid hub
  • epgp
Home › Forums › World of Warcraft General › Everything Else

for programming nerds only

Vitriola's picture

Vitriola — Fri, 02/26/2010 - 13:48

I saw a very interesting programming challenge which was to write a script that finds the longest repeated substring in a given text.

Repeated
substrings may not overlap. If more than one substring is repeated with
the same length, you may print any of them. If there is no repeated
substring, the result is an empty string (print nothing).

This is by no means a trivial task but I was amazed at the elegance of the solution once it was hammered through.  The final test was to run it against "war and peace" to find the longest repeated substring.  FYI, that substring turned out to be ", which under his leadership will be directed against the redoubt and come into line with the rest of the forces.  A".  (I got the book in text format from http://www.gutenberg.org)

Ruby code follows:

# Finds the longest, non-overlapping repeated substring in its input.
#
# The basic technique used by this solution is to create an array of
# all suffixes of the data.  So if the input were "banana", the array
# would contain ["banana", "anana", "nana", "ana", "na", "a"].  Then
# we sort this array, so it would now contain ["a", "ana", "anana",
# "banana", "na", "nana"].  Finally we can compare neighboring entries
# in the array to see if they share a long enough prefix to beat the
# current best.

# Extra care must be taken if the substrings are not allowed to
# overlap.  Consider the input "ananana"; the longest non-overlapping
# substring is "ana".  The array of sorted suffixes of is ["a", "ana",
# "anana", "ananana", "na", "nana", "nanana"].  The 2nd and 3rd items
# can only have a match of "an" because the "ana" would overlap, and
# the same is true with the 3rd and 4th items.  However by comparing
# the 2nd and 4th items we can get the desired result of "ana".  So
# under certain circumstances we have to compare an item with more
# than just its immediate predecessor.

# This program seems to run reasonably fast.  It should run in O(n *
# log n) time in most cases, assuming that Array's sort method
# provides that performance.  Due to the rare cases when the program
# cannot just compare an item and its immediate predecessor, there may
# be some strange cases where it requires O(n ** 2).  Because Ruby
# allows a computed substring to share the data with the original
# string (until one of the strings is altered, i.e., "copy on write"),
# the memory used is linear to the input size.


# returns the maximum of the two parameters
def max(a, b)
  a >= b ? a : b
end


# Return the longest common prefix between two strings.  If max is
# specified then the longest common prefix cannot exceed it
def longest_common_prefix(s1, s2, max = nil)
  l1, l2 = s1.size, s2.size
  min = l1 < l2 ? l1 : l2
  min = min < max ? min : max if max
  min.times do |i|
    return s1.slice(0, i) if s1[i] != s2[i]
  end
  return s1.slice(0, min)
end


# Returns the longest repeated substring in a given string.
def longest_repeated_substring(string)
  size = string.length

  # put every possible suffix into an array
  suffixes = Array.new(size)
  size.times do |i|
    suffixes[i] = string.slice(i, size)
  end

  # sort the array of suffixes, so common substrings (i.e., prefixes
  # of suffixes) will be found in neighboring elements of the array
  suffixes.sort!

  best = ""
  at_least_size = 1    # the size to meet or exceed to be the new best
  distance = nil
  neighbors_to_check = 1

  # compare pairs of consecutive suffixes and see how much initial
  # commonality there is
  # (size - 1).times do |i|
  (1...size).each do |i|
    # p [i, neighbors_to_check]
    s1 = suffixes[i]

    # generally we will only need to compare the ith item and the one
    # preceding it; however if we were in a position to reject a long
    # enough common substring due to overlap issues, then we may have
    # to compare an ith item with additional preceding items;
    # neighbors_to_check tracks how many neighbors we need to check
    neighbors_to_check.downto(1) do |neighbor|
      s2 = suffixes[i - neighbor]

      # make sure that these to suffixes further apart than the size
      # of the current best; we don't explicitly track the index of
      # these suffixes, but since all suffixes go to the end of the
      # initial string, the size can be used as a proxy
      distance = (s1.size - s2.size).abs
      if distance < at_least_size
        if s1.size >= at_least_size &&
            s2.size >= at_least_size &&
            s1.slice(0, at_least_size) == s2.slice(0, at_least_size)
          neighbors_to_check = max(neighbors_to_check, neighbor + 1)
        else
          neighbors_to_check = neighbor
        end
        next
      end

      # if neighboring suffixes don't at least match as far as the best,
      # no need to check more carefully
      unless s1.slice(0, at_least_size) == s2.slice(0, at_least_size)
        neighbors_to_check = neighbor
        next
      end

      # get the longest common prefix that's no larger than distance,
      # since at that point the substrings overlap
      best = longest_common_prefix(s1, s2, distance)
      at_least_size = best.size + 1
      if best.size == distance
        neighbors_to_check = max(neighbors_to_check, neighbor + 1)
      else
        neighbors_to_check = neighbor
      end
    end
  end

  best
end

open("c:\\Belfry\\WarAndPeace.txt") do |f| @string = f.read end   
result = longest_repeated_substring(@string)
puts result && "\"#{result}\" (#{result.length} characters)" || "none"

‹ Most awesome video ever made!Pacific NW Downfaller's!! ›
  • Everything Else
Groups:
  • Kitten Brigade
  • Raid: !!11
  • Rupta's Random Raids
garreth's picture

That's pretty kick ass :) How

garreth — Fri, 02/26/2010 - 13:58

That's pretty kick ass :) How long did this take to run on a modern computer?

Vitriola's picture

To do war and peace, less

Vitriola — Fri, 02/26/2010 - 16:10

To do war and peace, less than a minute.

Random image

Knoppix in Bora Bora

Navigation

  • Content
  • Raid Parses
  • The Downfall Handbook
  • User list
  • Polls
  • Groups
  • Popular content

Who's new

  • Babyconan
  • Grantlius
  • Ecology
  • fumabomb
  • firstslacker

Latest image

PMS Logo for Legion

Archives

  • September 2016 (1)
  • August 2016 (5)
  • July 2016 (8)
  • June 2016 (2)
  • May 2016 (1)
  • March 2016 (3)
  • February 2016 (2)
  • January 2016 (5)
  • August 2015 (1)
  • May 2015 (3)

Recent comments

  • App closed with extreme
    7 hours 17 min ago
  • Guild App closed at his
    4 weeks 1 day ago
  • Once we get the new site,
    7 weeks 1 day ago
  • Is the time frame posted in server time?
    7 weeks 2 days ago
  • Awesome. Thanks. Once I
    9 weeks 1 day ago
  • I'd be happy to stick around
    9 weeks 1 day ago
  • Possibly
    9 weeks 1 day ago
  • More necro
    10 weeks 2 days ago
  • Story time!
    11 weeks 3 days ago
  • I;ve downladed drop box, if
    13 weeks 17 hours ago

Active forum topics

  • April 1st 2006
  • Nostalarious: "Blizzard wants Vanilla servers"
  • Galaga 13/13 Heroic by Deposing A Tyrant
  • Poker Face Achieves in one night! Glory of Hellfire Raider
  • Glory of The Hellfire Raider
  • Galaga Destroys Heroic Archimonde AOTC 12/13H
  • Galaga takes out Thomas the Train (7/10 H BRF)
more

Recent blog posts

  • So I guess this is the end...
  • Shaedo's Soloing Extravaganza
  • Shaedo UI Extravaganza
  • Big site updates this weekend!
  • This is for all the Hearthstone players out there
  • My First Full Day on Beta
  • State Of <Downfall> 2014
  • Scaron taking a break
  • How Gorgonnash's Connections went
  • Let's Talk about Mythic Raids
more

Poll

What do you think about no flying for an entire expansion?
YES) It makes me feel more immersed in the game lore.
15%
YES) It encourages more PvP! (Please explain how in comments)
5%
YES) I miss riding my ground mounts.
9%
MAYBE) If you make all of my flying mounts able to be ground too.
8%
MAYBE) if ground mounts are sped up to 200% speed (or higher)
26%
NO) I don't have enough time when I'm on to be riding everywhere
7%
NO) I have spent too much time getting various levels of flying to be wasting it.
11%
NO) It encourages more PVP! (please explain in the comments)
0%
NO) I love flying.
19%
Total votes: 74
  • 38comments
  • Older polls

Popular content

All time:

  • Professor Putricide Strategy Page
  • Rotface Strategy Guide
  • Arthas, the Lich King Strategy Page
  • Sindragosa Strategy Page
  • Toravon the Ice Watcher Strategy Guide
  • home
  • forums
  • calendar
  • gallery
  • handbook
  • raid hub
  • epgp

I notice your oeuvre is monochromatic.
This site is proudly powered by Drupal.