title: codingbat wordEnds using regex
body: <p>I'm trying to solve <a href="http://codingbat.com/prob/p147538" rel="nofollow noreferrer"><code>wordEnds</code> from codingbat.com</a> using regex.</p>
  
  <blockquote>
    <p>Given a string and a non-empty word string, return a string made of each char just before and just after every appearance of the word in the string. Ignore cases where there is no char before or after the word, and a char may be included twice if it is between two words.</p>
  
  <pre><code>wordEnds("abcXY123XYijk", "XY") → "c13i"
  wordEnds("XY123XY", "XY") → "13"
  wordEnds("XY1XY", "XY") → "11"
  wordEnds("XYXY", "XY") → "XY"
  </code></pre>
  </blockquote>
  
  <p>This is the simplest as I can make it with my current knowledge of regex:</p>
  
  <pre><code>public String wordEnds(String str, String word) {
    return str.replaceAll(
       ".*?(?=word)(?&lt;=(.|^))word(?=(.|$))|.+"
         .replace("word", java.util.regex.Pattern.quote(word)),
       "$1$2"
    );
  }
  </code></pre>
  
  <p><code>replace</code> is used to place in the actual <code>word</code> string into the pattern for readability. <code>Pattern.quote</code> isn't necessary to pass their tests, but I think it's required for a proper regex-based solution.</p>
  
  <p>The regex has two major parts:</p>
  
  <ul>
  <li>If after matching as few characters as possible "<code>.*?</code>", <code>word</code> can still be found "<code>(?=word)</code>", then lookbehind to capture any character immediately preceding it "<code>(?&lt;=(.|^))</code>", match "<code>word</code>", and lookforward to capture any character following it "<code>(?=(.|$))</code>".
  
  <ul>
  <li>The initial "if" test ensures that the atomic lookbehind captures only if there's a <code>word</code></li>
  <li>Using lookahead to capture the following character doesn't consume it, so it can be used as part of further matching</li>
  </ul></li>
  <li>Otherwise match what's left "<code>|.+</code>"
  
  <ul>
  <li>Groups 1 and 2 would capture empty strings</li>
  </ul></li>
  </ul>
  
  <p>I think this works in all cases, but it's obviously quite complex. I'm just wondering if others can suggest a simpler regex to do this.</p>
  
  <p>Note: I'm not looking for a solution using <code>indexOf</code> and a loop. I want a regex-based <code>replaceAll</code> solution. I also need a working regex that passes all codingbat tests.</p>
  
  <hr>
  
  <p>I managed to reduce the occurrence of <code>word</code> within the pattern to just one.</p>
  
  <pre><code>".+?(?&lt;=(^|.)word)(?=(.?))|.+"
  </code></pre>
  
  <p>I'm still looking if it's possible to simplify this further, but I also have another question:</p>
  
  <ul>
  <li>With this latest pattern, I simplified <code>.|$</code> to just <code>.?</code> successfully, but if I similarly tried to simplify <code>^|.</code> to <code>.?</code> it doesn't work. Why is that?</li>
  </ul>