Privileged Strings

The set of privileged strings is defined recursively as follows.

  • All strings of length 0 or 1 are privileged.
  • A string s of length at least 2 is privileged, if there exists a shorter privileged string t that occurs in s exactly twice, once as a prefix and once as a suffix. Overlapping occurrences are counted as distinct.

For example, the strings aa, aaa and aba are privileged, but ab and aab are not.


An alphanumeric string.


All privileged substrings of the input string, each exactly once, in any order. The output can be given in your language's native array format (or closest equivalent), or printed one substring per line.

Fun fact

The number of strings in the output is always exactly length(s) + 1 (source).


Both functions and full programs are permitted. The lowest byte count wins, and standard loopholes are disallowed.

Test Cases

These are sorted first by length and then alphabetically, but any order is acceptable.

"" -> [""]
"a" -> ["","a"]
"abc" -> ["","a","b","c"]
"abcaaabccaba" -> ["","a","b","c","aa","cc","aaa","aba","abca","abcca","bccab","bcaaab","caaabc"]
"1010010110101010001101" -> ["","0","1","00","11","000","010","101","0110","1001","01010","10001","10101","010010","101101","0101010","1010101","01011010","10100101","1010001101","1101010100011","00101101010100","011010101000110"]
"CapsAndDigits111" -> ["","1","A","C","D","a","d","g","i","n","p","s","t","11","111","igi","sAndDigits"]


Here's a by-language leaderboard, courtesy of Martin Büttner.

To make sure that your answer shows up, please start your answer with a headline, using the following Markdown template:

# Language Name, N bytes

where N is the size of your submission. If you improve your score, you can keep old scores in the headline, by striking them through. For instance:

# Ruby, <s>104</s> <s>101</s> 96 bytes

function answersUrl(e){return"http://api.stackexchange.com/2.2/questions/"+QUESTION_ID+"/answers?page="+e+"&pagesize=100&order=desc&sort=creation&site=codegolf&filter="+ANSWER_FILTER}function getAnswers(){$.ajax({url:answersUrl(page++),method:"get",dataType:"jsonp",crossDomain:true,success:function(e){answers.push.apply(answers,e.items);if(e.has_more)getAnswers();else process()}})}function shouldHaveHeading(e){var t=false;var n=e.body_markdown.split("\n");try{t|=/^#/.test(e.body_markdown);t|=["-","="].indexOf(n[1][0])>-1;t&=LANGUAGE_REG.test(e.body_markdown)}catch(r){}return t}function shouldHaveScore(e){var t=false;try{t|=SIZE_REG.test(e.body_markdown.split("\n")[0])}catch(n){}return t}function getAuthorName(e){return e.owner.display_name}function process(){answers=answers.filter(shouldHaveScore).filter(shouldHaveHeading);answers.sort(function(e,t){var n=+(e.body_markdown.split("\n")[0].match(SIZE_REG)||[Infinity])[0],r=+(t.body_markdown.split("\n")[0].match(SIZE_REG)||[Infinity])[0];return n-r});var e={};var t=1;answers.forEach(function(n){var r=n.body_markdown.split("\n")[0];var i=$("#answer-template").html();var s=r.match(NUMBER_REG)[0];var o=(r.match(SIZE_REG)||[0])[0];var u=r.match(LANGUAGE_REG)[1];var a=getAuthorName(n);i=i.replace("{{PLACE}}",t++ +".").replace("{{NAME}}",a).replace("{{LANGUAGE}}",u).replace("{{SIZE}}",o).replace("{{LINK}}",n.share_link);i=$(i);$("#answers").append(i);e[u]=e[u]||{lang:u,user:a,size:o,link:n.share_link}});var n=[];for(var r in e)if(e.hasOwnProperty(r))n.push(e[r]);n.sort(function(e,t){if(e.lang>t.lang)return 1;if(e.lang<t.lang)return-1;return 0});for(var i=0;i<n.length;++i){var s=$("#language-template").html();var r=n[i];s=s.replace("{{LANGUAGE}}",r.lang).replace("{{NAME}}",r.user).replace("{{SIZE}}",r.size).replace("{{LINK}}",r.link);s=$(s);$("#languages").append(s)}}var QUESTION_ID=45497;var ANSWER_FILTER="!t)IWYnsLAZle2tQ3KqrVveCRJfxcRLe";var answers=[],page=1;getAnswers();var SIZE_REG=/\d+(?=[^\d&]*(?:&lt;(?:s&gt;[^&]*&lt;\/s&gt;|[^&]+&gt;)[^\d&]*)*$)/;var NUMBER_REG=/\d+/;var LANGUAGE_REG=/^#*\s*((?:[^,\s]|\s+[^-,\s])*)/
body{text-align:left!important}#answer-list,#language-list{padding:10px;width:290px;float:left}table thead{font-weight:700}table td{padding:5px}
<script src=https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js></script><link rel=stylesheet type=text/css href="//cdn.sstatic.net/codegolf/all.css?v=83c949450c8b"><div id=answer-list><h2>Leaderboard</h2><table class=answer-list><thead><tr><td></td><td>Author<td>Language<td>Size<tbody id=answers></table></div><div id=language-list><h2>Winners by Language</h2><table class=language-list><thead><tr><td>Language<td>User<td>Score<tbody id=languages></table></div><table style=display:none><tbody id=answer-template><tr><td>{{PLACE}}</td><td>{{NAME}}<td>{{LANGUAGE}}<td>{{SIZE}}<td><a href={{LINK}}>Link</a></table><table style=display:none><tbody id=language-template><tr><td>{{LANGUAGE}}<td>{{NAME}}<td>{{SIZE}}<td><a href={{LINK}}>Link</a></table>

  • \$\begingroup\$ Can the output be space-separated or does it have to be separated by newlines? (if printing) \$\endgroup\$
    – Sp3000
    Feb 8 '15 at 15:27
  • \$\begingroup\$ @Sp3000 It has to be either newlines, or however your language prints an array of strings. \$\endgroup\$
    – Zgarb
    Feb 8 '15 at 15:33
  • 2
    \$\begingroup\$ So you're asking us to check your privileged strings? \$\endgroup\$
    – imallett
    Feb 9 '15 at 0:52
  • \$\begingroup\$ why such a long definition? Couldn't you have equivalently written "a priveliged string is a string that begins and ends with the same symbol, said symbol not occuring anywhere else in the string"? =) \$\endgroup\$
    – Mints97
    Feb 9 '15 at 14:58
  • 1
    \$\begingroup\$ @Mints97 The shorter string t may not be a single symbol. For example, aaa is a privileged string, since it has aa as a prefix and suffix, and it occurs only twice. I added it as an example. \$\endgroup\$
    – Zgarb
    Feb 9 '15 at 15:02

.NET Regex, 31 bytes


The strings are captured in \1 in each match.


(?=                             # Lookahead. This makes it possible to catch overlapped
                                #   strings in \1.
    (                           # The captured group \1 for result.
        (                       # Match 0 or more occurrences.
            (?<=(\3\2|))        # Set \3 to the string from last \3 to the current position,
                                #   or an empty string for the first time.
            .+?(?<=\3)          # Match a shortest nonempty string so that the whole string
                                #   from the beginning to the current position ends with \3.

CJam, 33 45 39 38 bytes


Let's say it is printed without a trailing newline. So the trailing newline means an empty substring...


l              " Read one line. ";
_,,            " Get an array of 0 to array length-1. ";
\f>            " Get all nonempty suffixes. ";
{              " For each suffix: ";
    (          " Extract the first character. Say the first character X and the rest Y. ";
    0          " Push U = 0. ";
    {          " Do: ";
        :U     " Set variable U. ";
        2$>    " Z = Y with first U characters removed. ";
        2$2$#  " Find X in Y, or return -1 if not found. ";
        ):T    " Increment and save it in T. ";
        <+     " Append the first T characters of Z to X. ";
        UT+    " U += T. ";
        T      " Push T. ";
    }g         " ...while T is nonzero. ";
    ;\;        " Discard U and Y. ";
    N          " Append a newline. ";

Python 2, 179 173 164 bytes

exec"f=lambda s:len(s)<2or any(s[1:-1].count(s[:n])<(s[:n]==s[-n:])==f(s[:n])for n%s);g=lambda s:filter(f,{''}|{s[i:j+1]for i%sfor j%s})"%((" in range(len(s))",)*3)

Pretty bulky currently — I wonder if it's possible to combine the check and the substring generation somehow...

The lambda f is basically an is_privileged() function, combining the three conditions into one comparison (substring is privileged, appears twice, is suffix and prefix).


f=lambda s:len(s)<2or any(s[1:-1].count(s[:n])<(s[:n]==s[-n:])==f(s[:n])for n in range(len(s)))
g=lambda s:filter(f,{''}|{s[i:j+1]for i in range(len(s))for j in range(len(s))})

Python 3, 131 129 bytes

f=lambda s,p={''}:(len(s)<2or[f(s[1:]),f(s[:-1])]*any(s[:len(x)]==s[-len(x):]==x not in s[1:-1]for x in p))and p.add(s)or list(p)

This recursively finds privileged substrings, starting with the shortest ones and adding them to a set. The set is then used in determining whether longer substrings are privileged.

Unfortunately, it is a one-use-only function. Here's a corresponding full program (145 bytes):

f=lambda s:(len(s)<2or[f(s[1:]),f(s[:-1])]*any(s[:len(x)]==s[-len(x):]==x not in s[1:-1]for x in p))and p.add(s)

Python, 152 147 126 116 bytes

def n(s):
 for a in s:w+=a;t+=[w[w.rfind(w[-max(len(q)for q in t if w.endswith(q)):],0,-1):]]
 return t

As demonstrated in the linked paper, there is a unique privileged suffix for every prefix of a string. That suffix is of the form p·u·p where p is the longest previously found privileged substring which is a suffix of the prefix. (The search is the argument to max, which actually finds the length of p because it was easier to do max than longest.) Once p is found, the suffix itself is formed by searching for the second last occurrence of p in the prefix, using rfind constrained to not use the last character. We know that will work, because p is a previously found suffix. (A more rigorous proof of the algorithm can be derived from the paper.)

I'm pretty sure that this could be implemented in linear time using a suffix tree, instead of the quadratic cubic algorithm used above, but the above program is certainly fast enough in all the test cases, and handles a string of 2000 as in a little less than a second on my (not superpowered) laptop.


Ruby, 87

I do feel like there's a recursive regex solution here somewhere, but I'm not very good at those, so here's a very slow and long recursive function.


Algorithm is roughly the same as grc's, made slower (but shorter) by not special-casing single characters. A single-character string can be considered privileged due to having the empty string as a prefix, a suffix, and nowhere else.

The other interesting trick here is the use of $', a magic variable inherited from Perl that takes the value of the string after the most recent regular expression match. This gives me a short way of chopping the first character off of a string, although I gain almost all of those characters by having to set it up with s[/./] instead of s[0]. I use it again to check that the second substring match occurs at the end of the string.


J, 92 bytes

Takes a couple seconds on the longest input.

p checks if a string is privileged (with recursion), s checks every substring using p.

   s=.3 :'~.a:,,(<@#~p)\.\y'   


   s 'CapsAndDigits111'

   input =. '';'a';'ab';'abc';'abcaaabccaba';'1010010110101010001101';'CapsAndDigits111'

   s each input

   #@s each input NB. number of strings in outputs

   # each input NB. input lengths

JavaScript (ES6) 195

The P function recursively verifies that a string is privileged.
The F function tries every substring contained into the given string. Found strings are stored as keys of a hashtable to avoid duplicates.
At last, the keys are returned as output.

      return r
  return[a for(a in o)]

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.