Strong / weak typing and static / dynamic typing are orthogonal.
Strong / weak is about whether the type of a value matters, functionally speaking. In a weakly-typed language, you can take two strings that happen to be filled with digits and perform integer addition on them; in a strongly-typed language, this is an error (unless you cast or convert the values to the correct types first). Strong / weak typing is not a black-and-white thing; most languages are neither 100% strict nor 100% weak.
Static / dynamic typing is about whether types bind to values or to identifiers. In a dynamically-typed language, you can assign any value to any variable, regardless of type; static typing defines a type for every identifier, and assigning from a different type is either an error, or it results in an implicit cast. Some languages take a hybrid approach, allowing for statically declared types as well as untyped identifiers ('variant'). There is also type inference, a mechanism where static typing is possible without explicitly declaring the type of everything, by having the compiler figure out the types (Haskell uses this extensively, C# exposes it through the
Weak dynamic programming allows for a pragmatic approach; the language doesn't get in your way most of the time, but it won't step in when you're shooting yourself in the foot either. Strong static typing, by contrast, pushes the programmer to express certain expectations about values explicitly in the code, in a way that allows the compiler or interpreter to detect a class of errors. With a good type system, a programmer can define exactly what can and cannot be done to a value, and if, by accident, someone tries somethine undesired, the type system can often prevent it and show exactly where and why things go wrong.