// Programming Assignment 3 - Example Solution.
// CS 176, Summer 2001

#include "mas.h"


// This is an array algorithm, and we have a choice of two generic array
// algorithms:
//
//   do_something(a[1..n])		do_something(a[1..n])
//     something(a[1])			  do_something(a[1..n-1])
//     do_something(a[2..n])		  something(a[n])
//
// Both will work for this problem, but the left one is perhaps the easier of
// the two to understand, so we'll start with that.
//
// What does something(a[1]) do?  We need to find the maximal sample in a, and
// this is the only chance we get to look at a[1], so it seems sensible to
// expect something() to involve looking for a maximal sample containing a[1].
// Also, a[1] is the left end of the array, so searching for maximal samples
// containing a[1] requires looking to the right only, which simplifies the
// search.
//
// So much for something(); what does the rest of the program do?
// do_something(a[2..n]) returns the length of the maximal sample in a[2..n];
// something(a[1]) returns the maximal sample containing a[1]; the only thing
// left to do is compare the length of the two maximal samples and return the
// larger.


// A forward declaration for the subroutine implementing someting().

   static int fms(const int a[], int n, int start);


// The implementation of do_something().

sample find_maximal_sample(const int a[], int a_size) {

  // Given the integer array a of a_size elements, find and return a maximal
  // array sample in a.

  sample mas;
  mas.start = 0;
  mas.length = 0;

  // For each element a[start] in a, find the maximal array sample having its
  // left-most element at a[start].  Remember the largest maximal sample found.

     for (int start = 0; start < a_size; start++) {
       const int length = fms(a, a_size, start);
       if (mas.length < length) {
	 mas.start = start;
	 mas.length = length;
	 }
       }

  return mas;
  }


// The implementation of something().

static int fms(const int a[], int n, int start) {

  // Given the integer array a of n elements, find the size of the largest
  // array sample with the left-most array element at a[start].  Return the
  // sample's length.

  // For each element a[s] in the sample starting at a[start] and working
  // right...

     for (int s = start; s < n; s++)

       // Find the first occurence of the value in a[s] to the right of a[s] in
       // the sample.

	  for (int e = s + 1; e < n; e++)
	    if (a[s] == a[e]) {

	      // If found, the sample can be no longer than a[s .. e - 1]
	      // because of the repeated value at a[e]; reset n accordingly and
	      // stop searching for a[s].

		 n = e;
		 break;
  	         }

  // The maximal sample with its left element at a[start] ends at a[n].  Return
  // the sample's length.

  return n - start;
  }


// Now for the second generic array algorithm.  We are going to essentially use
// the same development approach we used for the first generic algorithm,
// except now we're interested in the maximal sample containing a[n] as its
// right-most element.
//
// The big difference between the first and second generic algorithms is that
// the second generic algorithm processes rest of the array first and then
// processes a[n], which means we can have do_something() do some extra work
// for us to simplify the search for the maximal sample containing a[n] as its
// rightmost element.
//
// We use two simple facts about samples in the algorithm:
//
//  1 A piece of a sample is a sample too.
//
//  2 A maximal sample can't grow larger and remain a sample.
//
// The extra work we require of do_something(a[1..n-1]) is to also remember the
// length of the largest sample ending at the right-end of the array.  With
// this length the search for the maximal sample containing a[n] becomes easy:
// one pass through the sample looking for a[n].  If we find a[n] in the
// right-most sample, we chop the sample and fact one assures us we still have
// a sample without anymore scanning; if we don't find it, fact 2 assures us we
// can stop at the end of the sample because adding more elements would make it
// a non-sample.
//
// With these observations, the code follows.


sample find_maximal_sample_two(const int a[], int a_size) {

  // Given the integer array a of a_size elements, find and return a maximal
  // array sample in a.

  // Initially both samples are empty.

     sample mas;
     mas.start = 0;
     mas.length = 0;

     int rightmost_length = 0;

   for (int end = 0; end < a_size; end++) {

     // Scan left from the end element until you hit the start of the rightmost
     // maximal sample or you find a match to the end element in the sample.

	int rightmost_start = end - 1;
	while ((end - rightmost_start <= rightmost_length) && 
	       (a[rightmost_start] != a[end]))
	  rightmost_start--;

     // Where ever the scan stopped is the start of the new rightmost maxiaml
     // sample; compute its length.

	rightmost_length = end - rightmost_start;

     // Compare the length of the rightmost maximal sample to the length of the
     // maximal sample and keep the larger of the two.

	if (mas.length < rightmost_length) {
	  mas.start = end - rightmost_length + 1;
	  mas.length = rightmost_length;
	  }
     }

  return mas;
  }




syntax highlighted by Code2HTML, v. 0.9