Advent of Code 2015: day 5, part 2 unknown false positives - rust

I'm working through the Advent of Code 2015 problems in order to practise my Rust skills.
Here is the problem description:
Realizing the error of his ways, Santa has switched to a better model of determining whether a string is naughty or nice. None of the old rules apply, as they are all clearly ridiculous.
Now, a nice string is one with all of the following properties:
It contains a pair of any two letters that appears at least twice in the string without overlapping, like xyxy (xy) or aabcdefgaa (aa), but not like aaa (aa, but it overlaps).
It contains at least one letter which repeats with exactly one letter between them, like xyx, abcdefeghi (efe), or even aaa.
For example:
qjhvhtzxzqqjkmpb is nice because is has a pair that appears twice (qj) and a letter that repeats with exactly one letter between them (zxz).
xxyxx is nice because it has a pair that appears twice and a letter that repeats with one between, even though the letters used by each rule overlap.
uurcxstgmygtbstg is naughty because it has a pair (tg) but no repeat with a single letter between them.
ieodomkazucvgmuy is naughty because it has a repeating letter with one between (odo), but no pair that appears twice.
How many strings are nice under these new rules?
This is what I've managed to come up with so far:
pub fn part2(strings: &[String]) -> usize {
strings.iter().filter(|x| is_nice(x)).count()
/* for s in [
String::from("qjhvhtzxzqqjkmpb"),
String::from("xxyxx"),
String::from("uurcxstgmygtbstg"),
String::from("ieodomkazucvgmuy"),
String::from("aaa"),
]
.iter()
{
is_nice(s);
}
0 */
}
fn is_nice(s: &String) -> bool {
let repeat = has_repeat(s);
let pair = has_pair(s);
/* println!(
"s = {}: repeat = {}, pair = {}, nice = {}",
s,
repeat,
pair,
repeat && pair
); */
repeat && pair
}
fn has_repeat(s: &String) -> bool {
for (c1, c2) in s.chars().zip(s.chars().skip(2)) {
if c1 == c2 {
return true;
}
}
false
}
fn has_pair(s: &String) -> bool {
// Generate all possible pairs
let mut pairs = Vec::new();
for (c1, c2) in s.chars().zip(s.chars().skip(1)) {
pairs.push((c1, c2));
}
// Look for overlap
for (value1, value2) in pairs.iter().zip(pairs.iter().skip(1)) {
if value1 == value2 {
// Overlap has occurred
return false;
}
}
// Look for matching pair
for value in pairs.iter() {
if pairs.iter().filter(|x| *x == value).count() >= 2 {
//println!("Repeat pair: {:?}", value);
return true;
}
}
// No pair found
false
}
However despite getting the expected results for the commented-out test values, my result when running on the actual puzzle input does not compare with community verified regex-based implementations. I can't seem to see where the problem is despite having thoroughly tested each function with known test values.
I would rather not use regex if at all possible.

I think has_pairs has a bug:
In the word aaabbaa, we have overlapping aa (at the beginning aaa), but I think you are not allowed to return false right away, because there is another - non-overlapping - aa at the end of the word.

Related

Is there an easy way to count booleans in Rust?

I've encountered a scenario in which I have a known, small number of boolean values, but I don't care about them individually, I just want to determine how many of them are true. It seems like there should be a fast way of doing this, but the best I can come up with is the naive solution:
let mut count: u8 = 0;
if a {
count += 1;
}
if b {
count += 1;
}
if c {
count += 1;
}
Is there a better way of doing this?
You could simply do:
let count = a as u8 + b as u8 + c as u8;
I'd like to point you to Michael Anderson's post, which gives a much nicer syntax than mine.
let a = true;
let b = false;
let c = true;
let d = true;
let e = false;
let total_true = [a, b, c, d, e].into_iter().filter(|b| *b).count();
println!("{} options are true!", total_true);
I've left my original answer below for posterity
Original Answer
One option is to put your booleans into a Vector, then filter based on if they're true or not.
let options = vec![true, false, true, false, false, false, true];
let total_true = options.into_iter()
.filter(|b| *b)
.collect::<Vec<bool>>()
.len();
println!("{} options are true!", total_true);
We need to convert Vec into an iter, so we can run filter on it.
Then, since filter checks for true boolean expressions, we simply deref our item as *b to get the non-borrowed boolean value.
Then we collect that back into a Vector, and get the length to find out how many met our criteria (of being true).
This gives you the total number of true booleans.

Swift - best practice to find the longest string at [String] array

I'm trying to find what is the most effective way to get the longest string in a string array. For example :
let array = ["I'm Roi","I'm asking here","Game Of Thrones is just good"]
and the outcome will be - "Game Of Thrones is just good"
I've tried using the maxElement func, tho it's give the max string in a alphabetic ideas(maxElement()).
Any suggestions? Thanks!
Instead of sorting which is O(n log(n)) for a good sort, use max(by:) which is O(n) on Array providing it a closure to compare string lengths:
Swift 4:
For Swift 4 you can get the string length with the count property on String:
let array = ["I'm Roi","I'm asking here","Game Of Thrones is just good"]
if let max = array.max(by: {$1.count > $0.count}) {
print(max)
}
Swift 3:
Use .characters.count on String to get the string lengths:
let array = ["I'm Roi","I'm asking here","Game Of Thrones is just good"]
if let max = array.max(by: {$1.characters.count > $0.characters.count}) {
print(max)
}
Swift 2:
Use maxElement on Array providing it a closure to compare string lengths:
let array = ["I'm Roi","I'm asking here","Game Of Thrones is just good"]
if let max = array.maxElement({$1.characters.count > $0.characters.count}) {
print(max)
}
Note: maxElement is O(n). A good sort is O(n log(n)), so for large arrays, this will be much faster than sorting.
You can use reduce to do this. It will iterate through your array, keeping track of the current longest string, and then return it when finished.
For example:
let array = ["I'm Roi","I'm asking here","Game Of Thrones is just good"]
if let longestString = array.reduce(Optional<String>.None, combine:{$0?.characters.count > $1.characters.count ? $0:$1}) {
print(longestString) // "Game Of Thrones is just good"
}
(Note that Optional.None is now Optional.none in Swift 3)
This uses an nil starting value to account for the fact that the array could be empty, as pointed out by #JHZ (it will return nil in that case). If you know your array has at least one element, you can simplify it to:
let longestString = array.reduce("") {$0.characters.count > $1.characters.count ? $0:$1}
Because it only iterates through each element once, it will quicker than using sort(). I did a quick benchmark and sort() appears around 20x slower (although no point in premature optimisation, I feel it is worth mentioning).
Edit: I recommend you go with #vacawama's solution as it's even cleaner than reduce!
Here you go:
let array = ["I'm Roi","I'm asking here","Game Of Thrones is just good"]
var sortedArr = array.sort() { $0.characters.count > $1.characters.count }
let longestEelement = sortedArr[0]
You can also practice with the use of Generics by creating this function:
func longestString<T:Sequence>(from stringsArray: T) -> String where T.Iterator.Element == String{
return (stringsArray.max {$0.count < $1.count}) ?? ""
}
Explanation: Create a function named longestString. Declar that there is a generic type T that implements the Sequence protocol (Sequence is defined here: https://developer.apple.com/documentation/swift/sequence). The function will return a single String (of course, the longest). The where clause explains that the generic type T should be limited to having elements of type String.
Inside the function, call the max function of the stringsArray by comparing the longest string of the elements inside. What will be returned is the longest String (an optional as it can be nil if the array is empty). If the longest string is nil then (use of ??) returns an empty string as the longest string instead.
Now call it:
let longestA = longestString(from:["Shekinah", "Chesedh", "Agape Sophia"])
If you get the hang of using generics, even if the strings are hidden inside objects, you can make use of the pattern of coding above. You can change the element to objects of the same class (Person for example).
Thus:
class Person {
let name: String
init(name: String){
self.name = name
}
}
func longestName<T:Sequence>(from stringsArray: T) -> String where T.Iterator.Element == Person{
return (stringsArray.max {$0.name.count < $1.name.count})?.name ?? ""
}
Then call the function like these:
let longestB = longestName(from:[Person(name: "Shekinah"), Person(name: "Chesedh"), Person(name: "Agape Sophia")])
You also get to rename your function based on the appropriateness of its use. You can tweak the pattern to return something else, like the object itself, or the length (count) of the String. And finally, becoming familiar with generics may improve your coding ability.
Now, with a little tweak again, you may extend further so that you can compare strings owned by many different types as long as they implement a common protocol.
protocol Nameable {
var name: String {get}
}
This defines a protocol named Nameable that requires those who implement to have a name variable of type String. Next, we define two different things that both implement the protocol.
class Person: Nameable {
let name: String
init(name: String){
self.name = name
}
}
struct Pet: Nameable {
let name: String
}
Then we tweak our generic function so that it requires that the elements must conform to Nameable, vastly different though they are.
func longestName<T:Sequence>(from stringsArray: T) -> String where T.Iterator.Element == Nameable{
return (stringsArray.max {$0.name.count < $1.name.count})?.name ?? ""
}
Let's collect the different objects into an array. Then call our function.
let myFriends: [Nameable] = [Pet(name: "Bailey"), Person(name: "Agape Sophia")]
let longestC = longestName(from: myFriends)
Lastly, after knowing "where" above and "Sequence" above, you may simply extend Sequence:
extension Sequence where Iterator.Element == String {
func topString() -> String {
self.max(by: { $0.count < $1.count }) ?? ""
}
}
Or the protocol type:
extension Sequence where Iterator.Element == Nameable {
func theLongestName() -> Nameable? {
self.max(by: { $0.name.count < $1.name.count })
}
}

How to make a function that compares strings?

I want to make a function which compares strings.
I don't want to use equal operators (==), I want it worked only with Swift language.
First I made a function which takes 2 strings, and returns bool type.
then I looped these strings with for in syntax.
And want to compare these characters, if strings have equal value, it should return true, if not, then false. Is there any better way?
func isEqual(str1:String, str2:String) -> Bool {
var result = false
for char in str1 {
}
for char2 in str2 {
}
//Compare characters.
return result
}
== works fine with Strings in Swift. For educational purposes
(as I conclude from your comment "because I'm practicing...")
you can implement it as:
func myStringCompare(str1 : String, str2 : String) -> Bool {
if count(str1) != count(str2) {
return false
}
for (c1, c2) in zip(str1, str2) {
if c1 != c2 {
return false
}
}
return true
}
zip(str1, str2) returns a sequence of pairs from the given
sequences, this is a convenient way to enumerate the strings
"in parallel".
Once you have understood how it works, you can shorten it,
for example to:
func myStringCompare(str1 : String, str2 : String) -> Bool {
return count(str1) == count(str2) && !contains(zip(str1, str2), { $0 != $1 })
}
Comparing the string length is necessary because the zip() sequence
terminates as soon as one of the strings is exhausted. Have a look at
#drewag's answer to In Swift I would like to "join" two sequences in to a sequence of tuples
for an alternative Zip2WithNilPadding sequence.
If you don't want to use the built-in zip() function (again for
educational/self-learning purposes!) then you can use the fact
that Strings are sequences, and enumerate them in parallel using
the sequence generator. This would work not only for strings but
for arbitrary sequences, as long as the underlying elements can
be tested for equality, so let's make it a generic function:
func mySequenceCompare<S : SequenceType where S.Generator.Element : Equatable>(lseq : S, rseq : S) -> Bool {
var lgen = lseq.generate()
var rgen = rseq.generate()
// First elements (or `nil`):
var lnext = lgen.next()
var rnext = rgen.next()
while let lelem = lnext, relem = rnext {
if lelem != relem {
return false
}
// Next elements (or `nil`):
lnext = lgen.next()
rnext = rgen.next()
}
// Are both sequences exhausted?
return lnext == nil && rnext == nil
}
Tests:
mySequenceCompare("xa", "xb") // false
mySequenceCompare("xa", "xa") // true
mySequenceCompare("a", "aa") // false
mySequenceCompare("aa", "a") // false
My solution differ a little as I didn't know about the zip operator, I guess is not as efficient as the one post by Martin great use of tuple.
Great question alphonse
func isEqual(str1:String, str2:String) -> Bool {
if count(str1) != count(str2){
return false
}
for var i = 0; i < count(str1); ++i {
let idx1 = advance(str1.startIndex,i)
let idx2 = advance(str2.startIndex,i)
if str1[idx1] != str2[idx2]{
return false
}
}
return true
}
As pointed by Martin each string needs its own index, as explained by him:
"The "trick" is that "🇩🇪" is an "extended grapheme cluster" and consists of two Unicode code points, but counts as one Swift character."
Link for more details about extended grapheme cluster

Is it possible to get a natural word after it has been stemmed?

I have a word play which after stemming has become plai. Now I want to get play again. Is it possible? I have used Porter's Stemmer.
Clearly not. Many different words, after being stemmed, can become plai: including play and playing.
Try it here: http://9ol.es/porter_js_demo.html
So if, given plai, it could have come from either word, it's not deterministic. Or do you want to get the set of all possible words that stem to plai?
Update: Qualtagh mentions some good ideas.
Stemmer is able to process artificial non-existing words. Would you like them to be returned as elements of a set of all possible words? How do you know that the word doesn't exist and shouldn't be returned?
As an option: find a dictionary of all words and their forms. Find a stem for every of them. Save this projection as a map: ( stem, list of all word forms ). So you'll be able to get the list of all word forms for a given stem.
UPD:
If you need all possible words including non-existing then I can offer such an algorithm (it's not checked, just a suggestion):
Porter stemming algorithm. We need a reversed version.
If the rule in straight algorithm has a form (m>1) E -> (delete last E) then the reversed rule would be "fork with E" which means we need to try alternative ways. E.g., in straight algorithm probate -> probat, in reversed we have two alternatives: probat -> { probat, probate }. Each of these alternatives should be separately processed further. Note that this is a set of alternatives, so we will process only distinct words. Such a rule would have the following form: A -> { , B, C }, which means "replace ending A in three alternative ways: leave as-is, with B and with C".
Step 5b: (m>1) *L -> { , +L } // Add L if there's L at the end.
Step 5a: (m>1) -> { , +E }
(m=1 and not *o) -> { , +E } // *o is a special condition, it's not *O.
Step 4: (m>1) *S or *T -> { , +ION }
(m>1) -> { , +AL, +ANCE, +ENCE, ..., +IVE, +IZE }
Step 3: (m>0) *AL -> { , +IZE }
(m>0) *IC -> { , +ATE, +ITI, +AL }
(m>0) -> { , +ATIVE, +FUL, +NESS }
Step 2: (m>0) *ATE -> { , ATIONAL } // Replace ATE.
(m>0) *TION -> { , +AL } // Add AL at the end.
(m>0) *ENCE -> { , ENCI } // Replace ENCE.
...
(m>0) *BLE -> { , BILITI } // Replace BLE.
Step 1c: (*v*) *I -> { , Y } // Replace I.
Step 1b: (m=1 and *oE) -> { , +D, delete last E and add ING } // *o is a special condition.
(*v*c and not (*L or *S or *Z)) -> { , add last consonant +ED, add last consonant + ING }
*IZE -> { , IZING, +D }
(*v*BLE) -> { , +D, delete last E and add ING }
*ATE -> { , ATING, +D }
(*v*) -> { , +ED, +ING }
(m>0) *EE -> { , +D }
Step 1a: *I -> { , +ES }
*SS -> { , +ES }
not *S -> { , +S }
The straight algorithm had to choose first longest rule. The reversed algorithm should use all the rules.
Example (straight):
Input: PLAYING
Step 1a doesn't match.
PLAYING -> PLAY (Step 1b)
PLAY -> PLAI (Step 1c)
m=0, so the steps 2-5 don't match.
Result: PLAI
Reversed:
Input: PLAI
m=0, so the steps 2-5 are skipped
Step 1c:
PLAI -> { PLAI, PLAY }
Step 1b:
PLAI -> { PLAI, PLAIED, PLAIING }
PLAY -> { PLAY, PLAYED, PLAYING }
Resulting set: { PLAI, PLAIED, PLAIING, PLAY, PLAYED, PLAYING }
Step 1a:
PLAI -> { PLAI, PLAIS, PLAIES }
PLAIED -> { PLAIED, PLAIEDS }
PLAIING -> { PLAIING, PLAIINGS }
PLAY -> { PLAY, PLAYS }
PLAYED -> { PLAYED, PLAYEDS }
PLAYING -> { PLAYING, PLAYINGS }
Resulting set: { PLAI, PLAIS, PLAIES, PLAIED, PLAIEDS, PLAIING, PLAIINGS, PLAY, PLAYS, PLAYED, PLAYEDS, PLAYING, PLAYINGS }
I've checked all those words at Michael Tontchev's link. The result for every of them is "plai" (note that the site doesn't accept upper-case input).

Convert String.Index to Int or Range<String.Index> to NSRange

So I've found issues relating to the case of converting NSRange to Range<String.Index>, but I've actually run into the opposite problem.
Quite simply, I have a String and a Range<String.Index> and need to convert the latter into an NSRange for use with an older function.
So far my only workaround has been to grab a substring instead like so:
func foo(theString: String, inRange: Range<String.Index>?) -> Bool {
let theSubString = (nil == inRange) ? theString : theString.substringWithRange(inRange!)
return olderFunction(theSubString, NSMakeRange(0, countElements(theSubString)))
}
This works of course, but it isn't very pretty, I'd much rather avoid having to grab a sub-string and just use the range itself somehow, is this possible?
If you look into the definition of String.Index you find:
struct Index : BidirectionalIndexType, Comparable, Reflectable {
/// Returns the next consecutive value after `self`.
///
/// Requires: the next value is representable.
func successor() -> String.Index
/// Returns the previous consecutive value before `self`.
///
/// Requires: the previous value is representable.
func predecessor() -> String.Index
/// Returns a mirror that reflects `self`.
func getMirror() -> MirrorType
}
So actually there is no way to convert it to Int and that for good reason. Depending on the encoding of the string the single characters occupy a different number of bytes. The only way would be to count how many successor operations are needed to reach the desired String.Index.
Edit The definition of String has changed over the various Swift versions but it's basically the same answer. To see the very current definition just CMD-click on a String definition in XCode to get to the root (works for other types as well).
The distanceTo is an extension which goes to a variety of protocols. Just look for it in the String source after the CMD-click.
let index: Int = string.startIndex.distanceTo(range.startIndex)
I don't know which version introduced it, but in Swift 4.2 you can easily convert between the two.
To convert Range<String.Index> to NSRange:
let range = s[s.startIndex..<s.endIndex]
let nsRange = NSRange(range, in: s)
To convert NSRange to Range<String.Index>:
let nsRange = NSMakeRange(0, 4)
let range = Range(nsRange, in: s)
Keep in mind that NSRange is UTF-16 based, while Range<String.Index> is Character based.
Hence you can't just use counts and positions to convert between the two!
In Swift 4, distanceTo() is deprecated. You may have to convert String to NSString to take advantage of its -[NSString rangeOfString:] method, which returns an NSRange.
Swift 4 Complete Solution:
OffsetIndexableCollection (String using Int Index)
https://github.com/frogcjn/OffsetIndexableCollection-String-Int-Indexable-
let a = "01234"
print(a[0]) // 0
print(a[0...4]) // 01234
print(a[...]) // 01234
print(a[..<2]) // 01
print(a[...2]) // 012
print(a[2...]) // 234
print(a[2...3]) // 23
print(a[2...2]) // 2
if let number = a.index(of: "1") {
print(number) // 1
print(a[number...]) // 1234
}
if let number = a.index(where: { $0 > "1" }) {
print(number) // 2
}
You can use this function and call it when ever you need convertion
extension String
{
func CnvIdxTooIntFnc(IdxPsgVal: Index) -> Int
{
return startIndex.distanceTo(IdxPsgVal)
}
}

Resources